~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 پیشفرض اولین کلاس والد
- یا همان 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 مشکلی ایجاد نشود.
نوشته و پژوهش شده توسط دکتر شاهین صیامی