درک مدل‌ها در Django: ساختار، کاربرد و ارتباط با پایگاه‌داده

این مقاله توضیحی روشن و ساختارمند درباره مدل‌های Django ارائه می‌دهد؛ بخشی کلیدی که مسئول تعریف و مدیریت داده‌ها در برنامه‌های Django است. مقاله توضیح می‌دهد که مدل‌ها چگونه به جدول‌های پایگاه‌داده نگاشت می‌شوند، فیلدها چگونه تعریف می‌شوند، Django چگونه به‌طور خودکار SQL تولید می‌کند، و چگونه باید مدل‌ها را در پروژه فعال کرد. یک مثال عملی نیز برای درک بهتر ارائه شده است.

مدل‌های جنگو، جدول پایگاه‌داده، ORM، فیلد مدلINSTALLED_APPS، makemigrationsmigrate

~22 دقیقه مطالعه • بروزرسانی ۱۹ اسفند ۱۴۰۴

مقدمه‌ای بر مدل‌های Django

در Django، مدل‌ها هسته اصلی لایه داده هستند. هر مدل ساختار، فیلدها و رفتار داده‌هایی را که قصد ذخیره‌سازی آن‌ها را دارید تعریف می‌کند. معمولاً هر مدل به یک جدول پایگاه‌داده نگاشت می‌شود و Django به‌طور خودکار یک ORM قدرتمند برای تعامل با داده‌ها فراهم می‌کند.

۱. اصول اولیه مدل‌ها در Django

هر مدل در Django یک کلاس پایتون است که از django.db.models.Model ارث‌بری می‌کند. هر ویژگی (attribute) این کلاس نشان‌دهنده یک فیلد پایگاه‌داده است و Django از این اطلاعات برای ساخت جدول مربوطه استفاده می‌کند.

نکات کلیدی:

  • هر مدل یک کلاس پایتون است.
  • هر ویژگی مدل یک ستون در پایگاه‌داده است.
  • Django به‌طور خودکار یک API دسترسی به پایگاه‌داده ایجاد می‌کند.

۲. مثال سریع

در اینجا یک مدل ساده Person را می‌بینید که دارای نام و نام خانوادگی است:


from django.db import models

class Person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)

در این مثال، first_name و last_name فیلدهای مدل هستند و Django هر کدام را به یک ستون در پایگاه‌داده تبدیل می‌کند.

جدول SQL تولیدشده

Django جدولی مشابه زیر ایجاد می‌کند (نمونه با سینتکس PostgreSQL):


CREATE TABLE myapp_person (
    "id" bigint NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
    "first_name" varchar(30) NOT NULL,
    "last_name" varchar(30) NOT NULL
);

نکات فنی

  • نام جدول myapp_person به‌صورت خودکار ساخته می‌شود اما قابل تغییر است.
  • Django به‌طور پیش‌فرض یک فیلد id به‌عنوان کلید اصلی اضافه می‌کند مگر اینکه آن را تغییر دهید.
  • SQL تولیدشده بسته به نوع پایگاه‌داده انتخاب‌شده در تنظیمات Django متفاوت خواهد بود.

۳. استفاده از مدل‌ها در پروژه Django

پس از تعریف مدل‌ها، باید Django را از وجود آن‌ها مطلع کنید. این کار با اضافه کردن نام اپلیکیشنی که فایل models.py در آن قرار دارد به INSTALLED_APPS انجام می‌شود.

مثال:

اگر مدل‌ها در myapp/models.py قرار دارند، تنظیمات باید شامل این مقدار باشد:


INSTALLED_APPS = [
    # ...
    "myapp",
    # ...
]

۴. اعمال تغییرات مدل‌ها در پایگاه‌داده

هر زمان مدل جدیدی اضافه کنید یا مدل‌های موجود را تغییر دهید، باید migrations ایجاد و اعمال کنید تا ساختار پایگاه‌داده به‌روزرسانی شود.

دستورات:

  • ایجاد migration:
  • python manage.py makemigrations
  • اعمال migration:
  • python manage.py migrate

این دستورات تضمین می‌کنند که ساختار پایگاه‌داده با مدل‌های شما هماهنگ باشد.

جمع‌بندی

مدل‌های Django ستون فقرات لایه داده در برنامه‌های Django هستند. با تعریف مدل‌ها به‌عنوان کلاس‌های پایتون، Django به‌طور خودکار جدول‌های پایگاه‌داده را ایجاد کرده، ORM قدرتمندی ارائه می‌دهد و مدیریت داده‌ها را ساده می‌کند. پس از افزودن مدل‌ها به INSTALLED_APPS و اجرای migrations، پروژه شما آماده ذخیره و مدیریت داده‌ها خواهد بود.

مقدمه‌ای بر فیلدهای مدل در Django

فیلدها مهم‌ترین بخش یک مدل در Django هستند. هر فیلد نشان‌دهنده یک ستون در پایگاه‌داده است و نوع داده، اعتبارسنجی، و نحوه نمایش آن را مشخص می‌کند. تنها بخش ضروری هر مدل، فهرست فیلدهایی است که تعریف می‌کنید.

۱. تعریف فیلدها در مدل

فیلدها به‌صورت ویژگی‌های کلاس تعریف می‌شوند. باید مراقب باشید نام فیلدها با متدهای داخلی مدل‌ها مانند save، delete یا clean تداخل نداشته باشد.

مثال:


from django.db import models

class Musician(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    instrument = models.CharField(max_length=100)

class Album(models.Model):
    artist = models.ForeignKey(Musician, on_delete=models.CASCADE)
    name = models.CharField(max_length=100)
    release_date = models.DateField()
    num_stars = models.IntegerField()

در این مثال، هر ویژگی یک فیلد پایگاه‌داده است و Django آن‌ها را به ستون‌های جدول تبدیل می‌کند.

۲. انواع فیلدها

هر فیلد باید نمونه‌ای از یکی از کلاس‌های فیلد Django باشد. Django از نوع فیلد برای تعیین موارد زیر استفاده می‌کند:

  • نوع ستون پایگاه‌داده (مثل INTEGER، VARCHAR، TEXT)
  • ویجت پیش‌فرض فرم (مثل <input type="text">)
  • قوانین اعتبارسنجی اولیه

Django ده‌ها نوع فیلد داخلی دارد و در صورت نیاز می‌توانید فیلدهای سفارشی نیز ایجاد کنید.

۳. گزینه‌های فیلدها (Field Options)

هر فیلد مجموعه‌ای از آرگومان‌های مخصوص به خود دارد. برای مثال، CharField نیاز به max_length دارد. علاوه بر این، مجموعه‌ای از گزینه‌های مشترک بین همه فیلدها وجود دارد.

مهم‌ترین گزینه‌ها:

✔ null

اگر True باشد، مقدار خالی به‌صورت NULL در پایگاه‌داده ذخیره می‌شود. مقدار پیش‌فرض False است.

✔ blank

اگر True باشد، مقدار خالی در فرم‌ها مجاز است. این گزینه مربوط به اعتبارسنجی است، نه پایگاه‌داده.

✔ choices

برای محدود کردن مقدار فیلد به مجموعه‌ای از گزینه‌ها استفاده می‌شود. در این حالت ویجت فرم به‌صورت <select> نمایش داده می‌شود.

مثال choices:


YEAR_IN_SCHOOL_CHOICES = [
    ("FR", "Freshman"),
    ("SO", "Sophomore"),
    ("JR", "Junior"),
    ("SR", "Senior"),
    ("GR", "Graduate"),
]

عنصر اول هر تاپل مقدار ذخیره‌شده در پایگاه‌داده است و عنصر دوم مقدار قابل‌نمایش.

دسترسی به مقدار قابل‌نمایش:


p.get_shirt_size_display()

۴. استفاده از Enumeration برای choices

Django امکان تعریف choices با استفاده از کلاس‌های enumeration را فراهم می‌کند که کد را خواناتر و تمیزتر می‌کند.

مثال:


class Runner(models.Model):
    MedalType = models.TextChoices("MedalType", "GOLD SILVER BRONZE")
    name = models.CharField(max_length=60)
    medal = models.CharField(blank=True, choices=MedalType, max_length=10)

جمع‌بندی

فیلدها بخش اصلی مدل‌های Django هستند و نوع داده، اعتبارسنجی و نحوه نمایش اطلاعات را تعیین می‌کنند. با استفاده از انواع فیلدها، گزینه‌های مشترک، و قابلیت choices، می‌توانید مدل‌هایی دقیق، قابل‌اعتماد و حرفه‌ای بسازید. Django ابزارهای قدرتمندی برای مدیریت داده‌ها فراهم می‌کند و فیلدها نقطه شروع این قدرت هستند.

۱. گزینه‌های فیلد در Django

فیلدهای مدل در Django مجموعه‌ای از گزینه‌ها دارند که نحوه ذخیره‌سازی، اعتبارسنجی و نمایش داده‌ها را کنترل می‌کنند. این گزینه‌ها به توسعه‌دهنده امکان می‌دهند رفتار مدل را دقیق‌تر تنظیم کند.

✔ default

مقدار پیش‌فرض فیلد را تعیین می‌کند. می‌تواند یک مقدار ثابت یا یک تابع قابل فراخوانی باشد. اگر تابع باشد، هنگام ایجاد هر شیء جدید اجرا می‌شود.

✔ db_default

مقدار پیش‌فرضی است که در سطح پایگاه‌داده تنظیم می‌شود. می‌تواند مقدار ثابت یا تابع پایگاه‌داده باشد.

اگر هر دو default و db_default تنظیم شده باشند، Django در کد پایتون از default استفاده می‌کند، اما db_default در درج داده خارج از ORM یا در migrations اعمال می‌شود.

✔ help_text

متنی کمکی برای نمایش در فرم‌ها و پنل مدیریت. حتی اگر فیلد در فرم استفاده نشود، برای مستندسازی مفید است.

✔ primary_key

اگر True باشد، فیلد به‌عنوان کلید اصلی مدل استفاده می‌شود.

اگر هیچ فیلدی کلید اصلی نباشد، Django به‌طور خودکار یک فیلد id اضافه می‌کند.

مثال رفتار کلید اصلی:


class Fruit(models.Model):
    name = models.CharField(max_length=100, primary_key=True)

fruit = Fruit.objects.create(name="Apple")
fruit.name = "Pear"
fruit.save()
# اکنون هر دو Apple و Pear در جدول وجود دارند

✔ unique

اگر True باشد، مقدار فیلد باید در کل جدول یکتا باشد.

۲. کلید اصلی خودکار در Django

Django به‌طور پیش‌فرض یک کلید اصلی خودکار (auto-increment) ایجاد می‌کند. نوع این فیلد از DEFAULT_AUTO_FIELD یا AppConfig.default_auto_field تعیین می‌شود.

مثال:


id = models.BigAutoField(primary_key=True)

اگر خودتان کلید اصلی تعریف کنید، Django فیلد id را اضافه نخواهد کرد.

۳. نام نمایشی فیلدها (Verbose Name)

بیشتر فیلدها یک آرگومان اختیاری به نام verbose_name دارند. اگر مقداردهی نشود، Django نام ویژگی را به یک عبارت خوانا تبدیل می‌کند.

مثال‌ها:


first_name = models.CharField("person's first name", max_length=30)
first_name = models.CharField(max_length=30)  # نام نمایشی: "first name"

برای فیلدهای رابطه‌ای باید از verbose_name به‌صورت keyword استفاده کنید:


poll = models.ForeignKey(Poll, on_delete=models.CASCADE, verbose_name="the related poll")
sites = models.ManyToManyField(Site, verbose_name="list of sites")
place = models.OneToOneField(Place, on_delete=models.CASCADE, verbose_name="related place")

۴. روابط در مدل‌های Django

Django سه نوع رابطه اصلی پایگاه‌داده را پشتیبانی می‌کند: یک‌به‌چند، چند‌به‌چند و یک‌به‌یک.

۴.۱ رابطه یک‌به‌چند (ForeignKey)

برای تعریف رابطه یک‌به‌چند از ForeignKey استفاده می‌شود. این رابطه زمانی کاربرد دارد که چند شیء به یک شیء والد مرتبط باشند.

مثال:


class Manufacturer(models.Model):
    pass

class Car(models.Model):
    manufacturer = models.ForeignKey(Manufacturer, on_delete=models.CASCADE)

می‌توانید روابط بازگشتی یا ارجاع به مدل‌هایی که هنوز تعریف نشده‌اند نیز ایجاد کنید.

۴.۲ رابطه چند‌به‌چند (ManyToManyField)

برای زمانی که هر شیء می‌تواند به چند شیء دیگر مرتبط باشد و بالعکس، از ManyToManyField استفاده می‌شود.

مثال:


class Topping(models.Model):
    pass

class Pizza(models.Model):
    toppings = models.ManyToManyField(Topping)

این فیلد باید فقط در یکی از مدل‌ها تعریف شود، نه هر دو.

۴.۳ رابطه یک‌به‌یک (OneToOneField)

برای زمانی که هر شیء دقیقاً به یک شیء دیگر مرتبط باشد، از OneToOneField استفاده می‌شود. این رابطه معمولاً برای پروفایل کاربران کاربرد دارد.

جمع‌بندی

Django ابزارهای قدرتمندی برای تعریف رفتار فیلدها و روابط بین مدل‌ها ارائه می‌دهد. گزینه‌هایی مانند default، db_default، unique و verbose_name کنترل دقیقی بر داده‌ها فراهم می‌کنند. همچنین روابط ForeignKey، ManyToManyField و OneToOneField امکان مدل‌سازی ساختارهای پیچیده پایگاه‌داده را فراهم می‌کنند.

مقدمه

در Django، روابط چندبه‌چند (ManyToMany) زمانی استفاده می‌شوند که هر شیء از یک مدل بتواند با چند شیء از مدل دیگر مرتبط باشد و بالعکس. در بسیاری از موارد، یک ManyToManyField ساده کافی است. اما گاهی لازم است اطلاعات بیشتری درباره خود رابطه ذخیره کنیم؛ برای مثال، تاریخ عضویت یک فرد در یک گروه.

برای این موارد، Django امکان استفاده از مدل واسط را فراهم می‌کند که با استفاده از گزینه through به ManyToManyField معرفی می‌شود.

۱. تعریف مدل واسط در رابطه چندبه‌چند

برای افزودن فیلدهای اضافی به رابطه، باید یک مدل واسط تعریف کنید و آن را به ManyToManyField معرفی کنید.

مثال کامل:


class Person(models.Model):
    name = models.CharField(max_length=128)

class Group(models.Model):
    name = models.CharField(max_length=128)
    members = models.ManyToManyField(Person, through="Membership")

class Membership(models.Model):
    person = models.ForeignKey(Person, on_delete=models.CASCADE)
    group = models.ForeignKey(Group, on_delete=models.CASCADE)
    date_joined = models.DateField()
    invite_reason = models.CharField(max_length=64)

    class Meta:
        constraints = [
            models.UniqueConstraint(
                fields=["person", "group"], name="unique_person_group"
            )
        ]

در این مثال، مدل Membership اطلاعات اضافی مانند تاریخ عضویت و دلیل دعوت را ذخیره می‌کند.

۲. محدودیت‌های مدل واسط

  • مدل واسط باید دقیقاً یک ForeignKey به مدل مبدا و یک ForeignKey به مدل مقصد داشته باشد.
  • اگر بیش از یک ForeignKey وجود داشته باشد، باید از through_fields استفاده کنید.
  • در روابط بازگشتی (رابطه مدل با خودش)، دو ForeignKey مجاز است.
  • اگر مدل واسط محدودیت یکتا نداشته باشد، امکان ایجاد چند رابطه تکراری وجود دارد.

۳. ایجاد رابطه چندبه‌چند با مدل واسط

برای ایجاد رابطه، باید نمونه‌ای از مدل واسط بسازید:


ringo = Person.objects.create(name="Ringo Starr")
paul = Person.objects.create(name="Paul McCartney")
beatles = Group.objects.create(name="The Beatles")

m1 = Membership(
    person=ringo,
    group=beatles,
    date_joined=date(1962, 8, 16),
    invite_reason="Needed a new drummer.",
)
m1.save()

اکنون می‌توانید اعضای گروه را مشاهده کنید:


beatles.members.all()

۴. استفاده از add()، create() و set()

در صورتی که مدل واسط فیلدهای ضروری داشته باشد، باید از through_defaults استفاده کنید:


beatles.members.add(john, through_defaults={"date_joined": date(1960, 8, 1)})
beatles.members.create(
    name="George Harrison",
    through_defaults={"date_joined": date(1960, 8, 1)}
)
beatles.members.set(
    [john, paul, ringo, george],
    through_defaults={"date_joined": date(1960, 8, 1)}
)

۵. حذف روابط

اگر مدل واسط محدودیت یکتا نداشته باشد، remove() تمام روابط تکراری را حذف می‌کند:


beatles.members.remove(ringo)

برای حذف همه روابط:


beatles.members.clear()

۶. کوئری‌زدن بر اساس مدل واسط

کوئری بر اساس مدل مرتبط:


Group.objects.filter(members__name__startswith="Paul")

کوئری بر اساس فیلدهای مدل واسط:


Person.objects.filter(
    group__name="The Beatles",
    membership__date_joined__gt=date(1961, 1, 1)
)

دسترسی مستقیم به مدل واسط:


ringos_membership = Membership.objects.get(group=beatles, person=ringo)
ringos_membership.date_joined
ringos_membership.invite_reason

دسترسی از طریق رابطه معکوس:


ringo.membership_set.get(group=beatles)

جمع‌بندی

مدل‌های واسط در روابط چندبه‌چند Django امکان ذخیره‌سازی اطلاعات اضافی درباره رابطه را فراهم می‌کنند. با استفاده از through، UniqueConstraint، و کوئری‌های پیشرفته، می‌توان ساختارهای داده‌ای پیچیده و حرفه‌ای ایجاد کرد. این قابلیت یکی از قدرتمندترین ویژگی‌های ORM جنگو است.

۱. روابط یک‌به‌یک (One-to-One Relationships)

برای تعریف رابطه یک‌به‌یک در Django از OneToOneField استفاده می‌شود. این رابطه زمانی کاربرد دارد که یک شیء دقیقاً با یک شیء دیگر مرتبط باشد.

کاربرد اصلی:

زمانی که یک مدل «گسترش‌دهنده» مدل دیگر است. برای مثال، مدل Restaurant می‌تواند یک OneToOneField به مدل Place داشته باشد، زیرا رستوران نوعی مکان است.

مثال:


class Restaurant(models.Model):
    place = models.OneToOneField(Place, on_delete=models.CASCADE)

این رابطه مشابه وراثت مدل‌هاست، زیرا Django در وراثت نیز یک رابطه یک‌به‌یک ایجاد می‌کند.

نکات مهم:

  • می‌توان روابط بازگشتی یا ارجاع به مدل‌های تعریف‌نشده ایجاد کرد.
  • parent_link یک آرگومان اختیاری برای OneToOneField است.
  • برخلاف گذشته، OneToOneField به‌طور خودکار primary key نمی‌شود.

۲. ارتباط مدل‌ها در فایل‌های مختلف

ارتباط مدل‌ها بین اپلیکیشن‌ها کاملاً مجاز است. کافی است مدل موردنظر را import کنید:


from geography.models import ZipCode

class Restaurant(models.Model):
    zip_code = models.ForeignKey(ZipCode, on_delete=models.SET_NULL, null=True)

یا استفاده از lazy reference:


zip_code = models.ForeignKey("geography.ZipCode", on_delete=models.SET_NULL)

این روش نیازی به import ندارد.

۳. محدودیت‌های نام فیلدها

Django برای جلوگیری از تداخل با سیستم ORM محدودیت‌هایی برای نام فیلدها دارد:

  • نام فیلد نمی‌تواند یک کلمه رزرو شده پایتون باشد (مثل pass).
  • نام فیلد نمی‌تواند شامل دو زیرخط پشت‌سرهم باشد (foo__bar غیرمجاز است).
  • نام فیلد نباید با زیرخط تمام شود.
  • نام فیلد نمی‌تواند check باشد.

در صورت نیاز می‌توان از db_column برای تعیین نام متفاوت ستون پایگاه‌داده استفاده کرد.

۴. ساخت فیلدهای سفارشی

اگر فیلدهای داخلی Django نیاز شما را برآورده نکنند، می‌توانید فیلد سفارشی بسازید. این موضوع در مستندات Django به‌طور کامل توضیح داده شده است.

۵. Meta Options

برای افزودن متادیتا به مدل از کلاس داخلی Meta استفاده می‌شود. این متادیتا شامل مواردی مانند ترتیب پیش‌فرض، نام جدول، و نام‌های قابل‌خواندن است.

مثال:


class Ox(models.Model):
    horn_length = models.IntegerField()

    class Meta:
        ordering = ["horn_length"]
        verbose_name_plural = "oxen"

۶. ویژگی objects (مدیر مدل)

هر مدل یک Manager به نام objects دارد که رابط اصلی برای اجرای کوئری‌هاست. Manager فقط از طریق کلاس مدل قابل دسترسی است، نه از طریق نمونه‌ها.

۷. متدهای مدل

می‌توانید متدهای سفارشی برای مدل تعریف کنید تا منطق مربوط به هر شیء را در خود مدل نگه دارید.

مثال:


class Person(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    birth_date = models.DateField()

    def baby_boomer_status(self):
        if self.birth_date < date(1945, 8, 1):
            return "Pre-boomer"
        elif self.birth_date < date(1965, 1, 1):
            return "Baby boomer"
        return "Post-boomer"

    @property
    def full_name(self):
        return f"{self.first_name} {self.last_name}"

متدهای مهمی که معمولاً باید تعریف شوند:

  • __str__(): نمایش خوانا برای شیء
  • get_absolute_url(): تعیین URL یکتا برای شیء

۸. override کردن متدهای save و delete

برای تغییر رفتار ذخیره‌سازی یا حذف، می‌توانید متدهای save() و delete() را override کنید.

مثال:


class Blog(models.Model):
    name = models.CharField(max_length=100)
    tagline = models.TextField()

    def save(self, **kwargs):
        do_something()
        super().save(**kwargs)
        do_something_else()

جلوگیری از ذخیره:


def save(self, **kwargs):
    if self.name == "Yoko Ono's blog":
        return
    super().save(**kwargs)

نکات مهم:

  • همیشه باید super().save() را فراخوانی کنید مگر اینکه عمداً بخواهید ذخیره انجام نشود.
  • برای پشتیبانی از آرگومان‌های جدید Django، از **kwargs استفاده کنید.
  • در bulk operations، متدهای save و delete فراخوانی نمی‌شوند.

جمع‌بندی

روابط یک‌به‌یک، محدودیت‌های نام‌گذاری، Meta options، متدهای مدل و override کردن رفتارهای ذخیره‌سازی از مهم‌ترین بخش‌های ORM Django هستند. با درک این مفاهیم می‌توانید مدل‌هایی حرفه‌ای، قابل‌گسترش و دقیق طراحی کنید.

۱. اجرای SQL سفارشی

Django امکان اجرای SQL خام را در متدهای مدل یا توابع سطح ماژول فراهم می‌کند. این قابلیت زمانی مفید است که نیاز به کنترل مستقیم پایگاه‌داده داشته باشید. برای جزئیات بیشتر، به مستندات «استفاده از SQL خام» مراجعه کنید.

۲. وراثت مدل‌ها در Django

وراثت مدل‌ها در Django مشابه وراثت کلاس‌های پایتون است. تنها شرط این است که کلاس پایه باید از django.db.models.Model ارث‌بری کند.

سه نوع وراثت:

  • کلاس‌های انتزاعی (Abstract Base Classes) – برای اشتراک‌گذاری فیلدهای مشترک بدون ایجاد جدول پایگاه‌داده.
  • وراثت چندجدولی (Multi-table Inheritance) – هر مدل جدول جداگانه دارد و Django بین آن‌ها رابطه OneToOne ایجاد می‌کند.
  • مدل‌های Proxy – فقط رفتار پایتونی مدل را تغییر می‌دهند، بدون تغییر در ساختار پایگاه‌داده.

۳. کلاس‌های انتزاعی

کلاس‌های انتزاعی زمانی استفاده می‌شوند که می‌خواهید فیلدهای مشترک را در چند مدل استفاده کنید، بدون اینکه جدول جداگانه‌ای ایجاد شود.

مثال:


class CommonInfo(models.Model):
    name = models.CharField(max_length=100)
    age = models.PositiveIntegerField()

    class Meta:
        abstract = True

class Student(CommonInfo):
    home_group = models.CharField(max_length=5)

مدل Student شامل فیلدهای name، age و home_group خواهد بود. مدل CommonInfo جدول پایگاه‌داده ندارد.

نکات:

  • می‌توان فیلدهای به ارث رسیده را override یا حذف کرد.
  • این روش برای اشتراک‌گذاری منطق و فیلدهای مشترک بسیار مناسب است.

۴. وراثت Meta

اگر مدل فرزند کلاس Meta نداشته باشد، Meta کلاس والد را به ارث می‌برد. برای گسترش Meta، باید آن را subclass کنید.

مثال:


class CommonInfo(models.Model):
    class Meta:
        abstract = True
        ordering = ["name"]

class Student(CommonInfo):
    class Meta(CommonInfo.Meta):
        db_table = "student_info"

Django مقدار abstract=False را برای مدل‌های فرزند تنظیم می‌کند مگر اینکه خودتان آن را True کنید.

وراثت چندگانه:

اگر از چند کلاس انتزاعی ارث‌بری کنید، فقط Meta کلاس اول به ارث می‌رسد مگر اینکه به‌طور صریح Meta را ترکیب کنید.

۵. related_name و related_query_name در کلاس‌های انتزاعی

اگر در کلاس انتزاعی از related_name استفاده کنید، باید از الگوهای %(app_label)s و %(class)s استفاده کنید تا نام‌ها یکتا شوند.

مثال:


class Base(models.Model):
    m2m = models.ManyToManyField(
        OtherModel,
        related_name="%(app_label)s_%(class)s_related",
        related_query_name="%(app_label)s_%(class)ss",
    )
    class Meta:
        abstract = True

Django این الگوها را با نام اپلیکیشن و نام کلاس فرزند جایگزین می‌کند.

۶. وراثت چندجدولی (Multi-table Inheritance)

در این نوع وراثت، هر مدل جدول جداگانه دارد و Django یک OneToOneField خودکار بین مدل‌ها ایجاد می‌کند.

مثال:


class Place(models.Model):
    name = models.CharField(max_length=50)
    address = models.CharField(max_length=80)

class Restaurant(Place):
    serves_hot_dogs = models.BooleanField(default=False)
    serves_pizza = models.BooleanField(default=False)

هر دو مدل Place و Restaurant قابل کوئری هستند.

دسترسی به مدل فرزند:


p = Place.objects.get(id=12)
p.restaurant  # اگر p یک Restaurant باشد

اگر p رستوران نباشد، خطای DoesNotExist رخ می‌دهد.

فیلد OneToOne خودکار:


place_ptr = models.OneToOneField(
    Place,
    on_delete=models.CASCADE,
    parent_link=True,
    primary_key=True,
)

می‌توانید این فیلد را override کنید.

جمع‌بندی

Django ابزارهای قدرتمندی برای وراثت مدل‌ها، مدیریت Meta، ساختارهای پیچیده داده و کنترل رفتار مدل‌ها فراهم می‌کند. با درک این مفاهیم می‌توانید مدل‌هایی حرفه‌ای، تمیز و قابل‌گسترش طراحی کنید.

۱. رفتار Meta در وراثت چندجدولی

در وراثت چندجدولی (Multi-table inheritance)، مدل فرزند Meta کلاس والد را به ارث نمی‌برد. دلیل آن این است که مدل والد یک جدول واقعی در پایگاه‌داده دارد و اعمال دوباره Meta می‌تواند باعث رفتارهای متناقض شود.

با این حال، دو ویژگی Meta در صورت عدم تعریف در مدل فرزند به ارث می‌رسند:

  • ordering
  • get_latest_by

غیرفعال‌کردن ordering والد:


class ChildModel(ParentModel):
    class Meta:
        ordering = []

۲. وراثت و روابط معکوس

در وراثت چندجدولی، Django یک OneToOneField ضمنی بین والد و فرزند ایجاد می‌کند. این فیلد نام پیش‌فرض reverse relation را مصرف می‌کند و ممکن است با روابط دیگر تداخل ایجاد کند.

مثال تداخل:


class Supplier(Place):
    customers = models.ManyToManyField(Place)

این خطا ایجاد می‌شود چون place_ptr قبلاً reverse name را گرفته است.

راه‌حل:


customers = models.ManyToManyField(Place, related_name="provider")

۳. مشخص‌کردن parent_link

Django به‌طور خودکار یک OneToOneField برای اتصال فرزند به والد ایجاد می‌کند. اگر بخواهید نام این فیلد را کنترل کنید، می‌توانید خودتان آن را تعریف کنید و parent_link=True بگذارید.

مثال:


class Restaurant(Place):
    place_ptr = models.OneToOneField(
        Place,
        on_delete=models.CASCADE,
        parent_link=True,
        primary_key=True,
    )

۴. Proxy Models

Proxy models زمانی استفاده می‌شوند که بخواهید رفتار پایتونی مدل را تغییر دهید بدون اینکه جدول جدیدی در پایگاه‌داده ایجاد شود.

مثال:


class MyPerson(Person):
    class Meta:
        proxy = True

    def do_something(self):
        pass

هر دو مدل Person و MyPerson روی یک جدول کار می‌کنند.

مثال استفاده:


p = Person.objects.create(first_name="foobar")
MyPerson.objects.get(first_name="foobar")

۵. تغییر ordering با Proxy


class OrderedPerson(Person):
    class Meta:
        ordering = ["last_name"]
        proxy = True

کوئری‌های Person بدون ترتیب هستند، اما OrderedPerson بر اساس last_name مرتب می‌شود.

۶. QuerySet همیشه مدل درخواست‌شده را برمی‌گرداند

اگر از Person کوئری بگیرید، Django هرگز MyPerson برنمی‌گرداند. Proxy جایگزین مدل اصلی نمی‌شود.

۷. محدودیت‌های پایه در Proxy Models

  • Proxy باید دقیقاً از یک مدل غیرانتزاعی ارث‌بری کند.
  • می‌تواند از چند مدل انتزاعی ارث‌بری کند (تا زمانی که فیلد تعریف نکنند).
  • می‌تواند از چند Proxy ارث‌بری کند اگر والد مشترک داشته باشند.

۸. Managerها در Proxy Models

اگر Manager تعریف نکنید، Managerهای والد به ارث می‌رسند. اگر Manager تعریف کنید، همان Manager پیش‌فرض می‌شود.

مثال:


class NewManager(models.Manager):
    pass

class MyPerson(Person):
    objects = NewManager()

    class Meta:
        proxy = True

افزودن Manager اضافی بدون جایگزینی پیش‌فرض:


class ExtraManagers(models.Model):
    secondary = NewManager()

    class Meta:
        abstract = True

class MyPerson(Person, ExtraManagers):
    class Meta:
        proxy = True

۹. تفاوت Proxy Models با Unmanaged Models

  • managed=False: برای مدل‌سازی viewها یا جدول‌های خارجی.
  • proxy=True: برای تغییر رفتار پایتونی بدون تغییر ساختار پایگاه‌داده.

۱۰. وراثت چندگانه

Django از وراثت چندگانه پشتیبانی می‌کند، اما قوانین Python اعمال می‌شود. فقط Meta اولین والد استفاده می‌شود.

اگر چند مدل دارای primary key مشابه باشند، خطا رخ می‌دهد.

راه‌حل:


class Article(models.Model):
    article_id = models.AutoField(primary_key=True)

class Book(models.Model):
    book_id = models.AutoField(primary_key=True)

class BookReview(Book, Article):
    pass

یا استفاده از یک والد مشترک:


class Piece(models.Model):
    pass

class Article(Piece):
    article_piece = models.OneToOneField(Piece, parent_link=True, on_delete=models.CASCADE)

class Book(Piece):
    book_piece = models.OneToOneField(Piece, parent_link=True, on_delete=models.CASCADE)

class BookReview(Book, Article):
    pass

جمع‌بندی

Django ابزارهای قدرتمندی برای مدیریت وراثت، روابط معکوس، Proxy models و ساختارهای پیچیده مدل‌ها ارائه می‌دهد. با درک این مفاهیم می‌توانید مدل‌هایی حرفه‌ای، انعطاف‌پذیر و مقیاس‌پذیر طراحی کنید.

۱. ممنوعیت “پنهان‌سازی” نام فیلد در Django

در وراثت معمولی پایتون، یک کلاس فرزند می‌تواند هر ویژگی والد را override کند. اما در Django این کار برای فیلدهای مدل مجاز نیست.

اگر یک مدل غیرانتزاعی فیلدی به نام author داشته باشد، هیچ مدل فرزندی نمی‌تواند دوباره فیلدی با همین نام تعریف کند. این کار باعث خطای FieldError می‌شود.

استثنا:

این محدودیت برای فیلدهایی که از مدل‌های انتزاعی به ارث رسیده‌اند وجود ندارد. این فیلدها را می‌توان:

  • با یک فیلد جدید override کرد،
  • یا با مقدار None حذف کرد.

هشدار مهم:

اگر یک Manager از مدل انتزاعی به ارث برسد و به فیلدی اشاره کند که شما override کرده‌اید، ممکن است باگ‌های پنهان ایجاد شود.

۲. فیلدهایی که ویژگی‌های اضافی ایجاد می‌کنند

برخی فیلدها ویژگی‌های اضافی روی مدل ایجاد می‌کنند. برای مثال:

  • ForeignKey یک ویژگی با پسوند _id ایجاد می‌کند.
  • همچنین related_name و related_query_name روی مدل مقصد ایجاد می‌شوند.

این ویژگی‌ها را نمی‌توان override کرد مگر اینکه خود فیلد اصلی تغییر کند یا حذف شود.

۳. چرا Django اجازه override فیلدها را نمی‌دهد؟

وراثت مدل‌ها در Django با وراثت پایتون تفاوت دارد، زیرا Django باید:

  • نمونه‌سازی مدل‌ها را مدیریت کند،
  • فیلدها را در Model.__init__ مقداردهی کند،
  • سریال‌سازی و ذخیره‌سازی پایگاه‌داده را کنترل کند.

این پیچیدگی‌ها باعث می‌شود override کردن فیلدهای والد مشکلات جدی ایجاد کند.

۴. وراثت چندگانه و ترتیب حل فیلدها

در وراثت چندگانه، Django فیلدهای انتزاعی را با ترتیب depth-first حل می‌کند، نه مانند MRO پایتون که breadth-first است.

این تفاوت فقط در ساختارهای پیچیده وراثت اهمیت دارد و بهتر است از چنین ساختارهایی پرهیز کنید.

۵. سازمان‌دهی مدل‌ها در یک پکیج

اگر تعداد مدل‌ها زیاد شود، می‌توانید آن‌ها را در یک پکیج models سازمان‌دهی کنید.

روش کار:

  1. فایل models.py را حذف کنید.
  2. دایرکتوری myapp/models/ را ایجاد کنید.
  3. فایل __init__.py را اضافه کنید.
  4. مدل‌ها را در فایل‌های جداگانه مثل organic.py و synthetic.py قرار دهید.

نمونه:


# myapp/models/__init__.py
from .organic import Person
from .synthetic import Robot

این روش باعث:

  • خوانایی بیشتر،
  • namespace تمیزتر،
  • کارکرد بهتر ابزارهای تحلیل کد

می‌شود.

۶. منابع تکمیلی

برای مطالعه بیشتر، بخش Models Reference در مستندات Django تمام APIهای مرتبط با مدل‌ها، فیلدها، روابط و QuerySetها را پوشش می‌دهد.

جمع‌بندی

Django برای جلوگیری از خطاهای پیچیده و ناسازگاری‌های پایگاه‌داده، اجازه override کردن فیلدهای مدل‌های غیرانتزاعی را نمی‌دهد. همچنین سازمان‌دهی مدل‌ها در یک پکیج و رعایت اصول وراثت می‌تواند ساختار پروژه را تمیزتر و قابل نگهداری‌تر کند.

نوشته و پژوهش شده توسط دکتر شاهین صیامی