/home/cach-to-chuc-models-va-auto-import-trong-python

Cách tổ chức models và auto import trong python

Published on | Updated

Chuyện là mình cần phải tái cấu trúc lại bộ code cho 1 dự án: Flask + MongoDB.

Đi dạo 1 hồi lòng vòng tham khảo mấy ông lớn, to nhất là ông django thì thấy hầu hết mọi người đều có thói quen bỏ hết các model trong 1 file models.py. Kiểu vầy

from django.db import models

class A(models.Model):
    name = models.CharField(max_length=255)
    slug = models.CharField(max_length=255)

class B(models.Model):
    name = models.CharField(max_length=255)
    slug = models.CharField(max_length=255)

class C(models.Model):
    name = models.CharField(max_length=255)
    slug = models.CharField(max_length=255)

okay, 1 file cũng gọn, khi import từ file khác để xài cũng ổn

# import lẻ
from models import A
b = A()

# chơi nguyên cụm
import models
b = models.A()

Cơ mà khi số lượng bảng lên vài chục, số lượng field lên đôi chục thì cái file này nó phình to ra, khó quản lý.
Lại thêm dăm ông dev nhảy vào sửa thì cũng ốm đòn.

postgresql/mysql thì đỡ, vì chôt lên chốt xuống thiết kế, sửa cũng mệt.

chứ như mình phang phập với MongoDB lại còn đẻ tính năng, sửa tính năng vì làm MVP sửa liên tọi thì mệt lắm, chồng chéo, conflict, ...

Nên với tinh thần dân PHP gốc nhảy sang mình bóc nhỏ ra, mỗi model 1 bảng 1 file.
Sản phầm cuối dùng kiểu vầy

models
|-- __init__.py
|-- A.py
|-- B.py
|-- C.py

giờ thì khỏi lo cái vấn đề trên rồi. Nhưng lại nhảy ra cái vấn đề khác: đó là import quá dài dòng.

Không như PHP auto include tất, composer làm hộ tận răng. Triết lý của Python là xài gì import lấy, cũng không hỗ trợ auto include như PHP, kiểu nó vầy. Nên thành ra tác phẩm của mình nó thành vầy.

from app.models.A import A
from app.models.B import B

# code here
a = A()
b = B()

hoặc lười hơn chút, trong file models/__init__.py mình import sẵn như này

from .A import A
from .B import B
from .X import X

nên khi dùng mình sẽ nhẹ nhàng hơn

from app import models
a = models.A
b = models.B

# hoặc
from app.models import A
a = A()

cơ mà vẫn chưa thỏa mãn cái sự lười của mình, vì mỗi khi mình thêm 1 bảng, mình lại phải sửa file __init__

nên mình mới nghĩ ra cách auto import hết các class của các file trong folder models vào __init__.py

Thành quả cuối cùng

# -\*- coding: utf-8 -\*-
'''
Auto import all Class in app.models folder
class name must be same as filename

Author: Dung Nguyen (nhymxu)
'''

import importlib
import inspect
import os
import pkgutil

pkg_dir = os.path.dirname(__file__)

for (module_loader, name, ispkg) in pkgutil.iter_modules([pkg_dir]):
    try:
        module = importlib.import_module('.' + name, __package__)
        locals().update({k: v for (k, v) in module.__dict__.items() if k == name and inspect.isclass(v)})
    except ImportError as err:
        print('Error:', err)

Tada, giờ xài thôi

from app import models
a = models.A
b = models.B

# hoặc
from app.models import A
a = A()

Bài viết này sẽ giúp tui tăng lương như thế nào?
Thực ra là méo =)) mình viết để note lại những cái mình đã phải mò, nghĩ để phục vụ cái sự lười của mình mà thôi.