درک Managerها در Django ORM: مدیرهای سفارشی، مدیر پیش‌فرض، مدیر پایه و الگوهای پیشرفتهٔ استفاده

این مقاله توضیح می‌دهد که Manager در Django چیست، چگونه می‌توان نام آن را تغییر داد، چگونه یک Manager سفارشی ساخت، چگونه get_queryset را بازنویسی کرد، تفاوت بین مدیر پیش‌فرض و مدیر پایه چیست، چرا Django برای دسترسی به روابط از base manager استفاده می‌کند، و چگونه می‌توان متدهای یک QuerySet سفارشی را از طریق Manager در دسترس قرار داد. Managerها نقطهٔ اصلی تعامل مدل‌ها با پایگاه داده هستند و درک آن‌ها برای نوشتن ORM تمیز و قدرتمند ضروری است.

Django Manager، مدیر سفارشی، get_querysetمدیر پیش‌فرض، مدیر پایه، QuerySetسفارشی، دسترسی به روابط، Django ORM

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

۱. Manager چیست؟

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

Manager مسئول موارد زیر است:

  • ساخت QuerySet
  • ارائهٔ عملیات سطح جدول
  • سفارشی‌سازی رفتار پیش‌فرض QuerySet

۲. تغییر نام Manager پیش‌فرض

اگر نمی‌خواهید از نام objects استفاده کنید، کافی است یک Manager با نام دلخواه تعریف کنید:


class Person(models.Model):
    people = models.Manager()

اکنون Person.objects خطا می‌دهد، اما Person.people.all() کار می‌کند.

۳. ساخت Manager سفارشی

می‌توانید با ارث‌بری از models.Manager یک Manager سفارشی بسازید.

۳.۱ افزودن متدهای سفارشی

این روش برای منطق سطح جدول مناسب است.


class PollManager(models.Manager):
    def with_counts(self):
        return self.annotate(num_responses=Coalesce(models.Count("response"), 0))

استفاده:


OpinionPoll.objects.with_counts()

متدهای Manager می‌توانند هر چیزی برگردانند، نه فقط QuerySet.

۳.۲ بازنویسی get_queryset()

برای تغییر QuerySet پیش‌فرض Manager، متد get_queryset() را بازنویسی کنید:


class DahlBookManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset().filter(author="Roald Dahl")

اتصال به مدل:


class Book(models.Model):
    objects = models.Manager()
    dahl_objects = DahlBookManager()

نتیجه:

  • Book.objects.all() → همهٔ کتاب‌ها
  • Book.dahl_objects.all() → فقط کتاب‌های رولد دال

۴. استفاده از چند Manager روی یک مدل

می‌توانید چندین Manager روی یک مدل داشته باشید:


class AuthorManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset().filter(role="A")

class EditorManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset().filter(role="E")

اتصال:


class Person(models.Model):
    people = models.Manager()
    authors = AuthorManager()
    editors = EditorManager()

اکنون:

  • Person.authors.all()
  • Person.editors.all()
  • Person.people.all()

۵. مدیر پیش‌فرض (default manager)

اولین Manager تعریف‌شده در مدل، مدیر پیش‌فرض است:


Model._default_manager

Django در عملیات داخلی مانند dumpdata از آن استفاده می‌کند.

می‌توانید آن را صریحاً تعیین کنید:


class Meta:
    default_manager_name = "my_manager"

۶. مدیر پایه (base manager)

مدیر پایه (_base_manager) برای دسترسی به آبجکت‌های مرتبط استفاده می‌شود، حتی اگر مدیر پیش‌فرض آن‌ها را فیلتر کرده باشد.

می‌توانید آن را تغییر دهید:


class Meta:
    base_manager_name = "my_base_manager"

نکتهٔ مهم: مدیر پایه نباید هیچ داده‌ای را فیلتر کند، وگرنه Django نمی‌تواند روابط را درست بازیابی کند.

۷. Managerها و دسترسی به روابط

وقتی از یک رابطه استفاده می‌کنید (مثلاً choice.question)، Django از base manager مدل مقصد استفاده می‌کند، نه default manager.

این کار تضمین می‌کند که آبجکت‌های فیلترشده نیز قابل بازیابی باشند.

۸. فراخوانی متدهای QuerySet سفارشی از Manager

اگر یک QuerySet سفارشی ساختید، باید متدهای آن را در Manager نیز تعریف کنید:


class PersonQuerySet(models.QuerySet):
    def authors(self):
        return self.filter(role="A")

class PersonManager(models.Manager):
    def get_queryset(self):
        return PersonQuerySet(self.model, using=self._db)

    def authors(self):
        return self.get_queryset().authors()

اکنون:


Person.people.authors()

جمع‌بندی

Managerها ابزار قدرتمندی برای کنترل نحوهٔ تعامل مدل‌ها با پایگاه داده هستند. می‌توانید آن‌ها را تغییر نام دهید، سفارشی کنید، چندین Manager تعریف کنید، QuerySet پیش‌فرض را بازنویسی کنید و متدهای QuerySet سفارشی را از طریق Manager در دسترس قرار دهید. درک تفاوت بین default manager و base manager برای جلوگیری از رفتارهای غیرمنتظره ضروری است.

۱. ساخت Manager با استفاده از متدهای QuerySet

به‌جای اینکه متدهای QuerySet را روی Manager تکرار کنید، Django متدی به نام QuerySet.as_manager() ارائه می‌دهد که یک Manager می‌سازد و تمام متدهای مناسب QuerySet را روی آن کپی می‌کند.

مثال:


class Person(models.Model):
    ...
    people = PersonQuerySet.as_manager()

این Manager تقریباً همانند Manager سفارشی مثال قبلی عمل می‌کند.

چه متدهایی کپی می‌شوند؟

  • متدهای عمومی (public) → کپی می‌شوند.
  • متدهای خصوصی (با _ شروع می‌شوند) → کپی نمی‌شوند.
  • متدهایی که queryset_only = False دارند → همیشه کپی می‌شوند.
  • متدهایی که queryset_only = True دارند → هرگز کپی نمی‌شوند.

مثال کامل:


class CustomQuerySet(models.QuerySet):
    def public_method(self):
        return  # روی Manager و QuerySet موجود است

    def _private_method(self):
        return  # فقط روی QuerySet

    def opted_out_public_method(self):
        return
    opted_out_public_method.queryset_only = True  # فقط روی QuerySet

    def _opted_in_private_method(self):
        return
    _opted_in_private_method.queryset_only = False  # روی Manager هم کپی می‌شود

۲. استفاده از from_queryset برای ساخت Managerهای ترکیبی

اگر هم Manager سفارشی می‌خواهید و هم QuerySet سفارشی، از Manager.from_queryset() استفاده کنید.

مثال:


class CustomManager(models.Manager):
    def manager_only_method(self):
        return

class CustomQuerySet(models.QuerySet):
    def manager_and_queryset_method(self):
        return

class MyModel(models.Model):
    objects = CustomManager.from_queryset(CustomQuerySet)()

می‌توانید کلاس تولیدشده را ذخیره کنید:


MyManager = CustomManager.from_queryset(CustomQuerySet)

class MyModel(models.Model):
    objects = MyManager()

۳. رفتار Managerها در ارث‌بری مدل‌ها

قوانین اصلی:

  • Managerهای کلاس والد به کلاس فرزند ارث می‌رسند.
  • اگر هیچ Managerی تعریف نشده باشد، Django به‌طور خودکار objects می‌سازد.
  • Manager پیش‌فرض:
    • یا همان Manager تعیین‌شده در Meta.default_manager_name
    • یا اولین Manager تعریف‌شده در مدل
    • یا Manager پیش‌فرض اولین کلاس والد

مثال با کلاس انتزاعی:


class AbstractBase(models.Model):
    objects = CustomManager()
    class Meta:
        abstract = True

حالت ۱: استفاده مستقیم


class ChildA(AbstractBase):
    pass  # CustomManager مدیر پیش‌فرض است

حالت ۲: تعریف مدیر پیش‌فرض جدید


class ChildB(AbstractBase):
    default_manager = OtherManager()

حالت ۳: افزودن Managerهای جدید بدون تغییر مدیر پیش‌فرض

راه‌حل: یک کلاس والد دیگر اضافه کنید.


class ExtraManager(models.Model):
    extra_manager = OtherManager()
    class Meta:
        abstract = True

class ChildC(AbstractBase, ExtraManager):
    pass

در این حالت:

  • مدیر پیش‌فرض: CustomManager
  • مدیر اضافی: extra_manager

نکته مهم:

نمی‌توانید متدهای Manager را روی مدل انتزاعی فراخوانی کنید:


AbstractBase.objects.do_something()  # خطا

اما روی مدل واقعی مجاز است:


ChildA.objects.do_something()  # صحیح

۴. نکات مهم پیاده‌سازی Manager سفارشی

Django در برخی عملیات‌ها از Manager یک کپی سطحی (shallow copy) می‌سازد. بنابراین Manager شما باید قابل کپی باشد.

مثال:


import copy
manager = MyManager()
my_copy = copy.copy(manager)  # باید کار کند

اگر متدهای خصوصی مانند __getattr__ را تغییر دهید، ممکن است Manager غیرقابل‌کپی شود.

جمع‌بندی

با استفاده از as_manager و from_queryset می‌توانید بدون تکرار کد، Managerهای قدرتمند و تمیز بسازید. Django در ارث‌بری مدل‌ها رفتار دقیقی برای Managerها دارد و رعایت اصول آن از بروز خطا جلوگیری می‌کند. همچنین باید مطمئن شوید Manager شما قابل کپی باشد تا در Queryهای داخلی Django مشکلی ایجاد نشود.

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