Django 2.1

Managers




django

Managers

class Manager [source]

एक Manager वह इंटरफ़ेस है जिसके माध्यम से Django मॉडल को डेटाबेस क्वेरी ऑपरेशन प्रदान किए जाते हैं। Django एप्लिकेशन में प्रत्येक मॉडल के लिए कम से कम एक Manager मौजूद है।

Manager कक्षाएं काम करने का तरीका प्रश्नों को बनाने में प्रलेखित है; यह दस्तावेज़ विशेष रूप से मॉडल विकल्पों पर छूता है जो Manager व्यवहार को अनुकूलित करता है।

प्रबंधक के नाम

डिफ़ॉल्ट रूप से, Django प्रत्येक Django मॉडल वर्ग में नाम objects साथ एक Manager जोड़ता है। हालाँकि, यदि आप objects को फ़ील्ड नाम के रूप में उपयोग करना चाहते हैं, या यदि आप Manager लिए objects के अलावा किसी अन्य नाम का उपयोग करना चाहते हैं, तो आप इसे प्रति-मॉडल के आधार पर नाम बदल सकते हैं। किसी दिए गए वर्ग के लिए Manager नाम बदलने के लिए, उस मॉडल पर एक प्रकार के मॉडल की विशेषता को परिभाषित करें। models.Manager() । उदाहरण के लिए:

from django.db import models

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

इस उदाहरण मॉडल का उपयोग करके, Person.objects एक AttributeError अपवाद उत्पन्न करेगा, लेकिन Person.people.all() सभी Person वस्तुओं की एक सूची प्रदान करेगा।

कस्टम प्रबंधक

आप किसी विशेष मॉडल में कस्टम Manager उपयोग कर आधार Manager वर्ग का विस्तार कर सकते हैं और अपने मॉडल में अपने कस्टम Manager को तत्काल बदल सकते हैं।

दो कारण हैं QuerySet आप एक Manager को अनुकूलित कर सकते हैं: अतिरिक्त Manager विधियों को जोड़ने के लिए, और / या प्रारंभिक QuerySet को संशोधित करने के लिए Manager रिटर्न देता है।

अतिरिक्त प्रबंधक विधियाँ जोड़ना

अतिरिक्त Manager विधियों को जोड़ना आपके मॉडल में "तालिका-स्तरीय" कार्यक्षमता जोड़ने का पसंदीदा तरीका है। ("पंक्ति-स्तरीय" कार्यक्षमता के लिए - अर्थात, ऐसे कार्य जो किसी मॉडल ऑब्जेक्ट के एकल उदाहरण पर कार्य करते हैं - मॉडल विधियों का उपयोग करें, न कि कस्टम तरीके का।)

एक कस्टम Manager विधि आपके इच्छित कुछ भी वापस कर सकती है। यह एक QuerySet वापस करने के लिए नहीं है।

उदाहरण के लिए, यह कस्टम Manager एक तरीका with_counts() प्रदान करता है, जो सभी OpinionPoll ऑब्जेक्ट्स की एक सूची देता है, प्रत्येक एक अतिरिक्त num_responses विशेषता के साथ होता है जो एक समग्र क्वेरी का परिणाम है:

from django.db import models

class PollManager(models.Manager):
    def with_counts(self):
        from django.db import connection
        with connection.cursor() as cursor:
            cursor.execute("""
                SELECT p.id, p.question, p.poll_date, COUNT(*)
                FROM polls_opinionpoll p, polls_response r
                WHERE p.id = r.poll_id
                GROUP BY p.id, p.question, p.poll_date
                ORDER BY p.poll_date DESC""")
            result_list = []
            for row in cursor.fetchall():
                p = self.model(id=row[0], question=row[1], poll_date=row[2])
                p.num_responses = row[3]
                result_list.append(p)
        return result_list

class OpinionPoll(models.Model):
    question = models.CharField(max_length=200)
    poll_date = models.DateField()
    objects = PollManager()

class Response(models.Model):
    poll = models.ForeignKey(OpinionPoll, on_delete=models.CASCADE)
    person_name = models.CharField(max_length=50)
    response = models.TextField()

इस उदाहरण के साथ, आप num_responses विशेषताओं के साथ num_responses ऑब्जेक्ट्स की उस सूची को वापस करने के लिए OpinionPoll.objects.with_counts() का उपयोग OpinionPoll.objects.with_counts()

इस उदाहरण पर ध्यान देने वाली एक और बात यह है कि Manager विधियाँ मॉडल क्लास प्राप्त करने के लिए self.model तक पहुँच self.model हैं, जिसमें वे संलग्न हैं।

एक प्रबंधक के प्रारंभिक QuerySet संशोधित करना

एक Manager का आधार QuerySet सिस्टम में सभी ऑब्जेक्ट देता है। उदाहरण के लिए, इस मॉडल का उपयोग करना:

from django.db import models

class Book(models.Model):
    title = models.CharField(max_length=100)
    author = models.CharField(max_length=50)

... विवरण Book.objects.all() डेटाबेस में सभी पुस्तकों को लौटा देगा।

आप Manager.get_queryset() विधि को ओवरराइड करके Manager के आधार QuerySet को ओवरराइड कर सकते हैं। QuerySet get_queryset() आपके द्वारा आवश्यक गुणों के साथ एक QuerySet वापस करना चाहिए।

उदाहरण के लिए, निम्न मॉडल में दो Manager - एक जो सभी वस्तुओं को लौटाता है, और एक वह जो केवल रोआल्ड डाहल द्वारा पुस्तकें लौटाता है:

# First, define the Manager subclass.
class DahlBookManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset().filter(author='Roald Dahl')

# Then hook it into the Book model explicitly.
class Book(models.Model):
    title = models.CharField(max_length=100)
    author = models.CharField(max_length=50)

    objects = models.Manager() # The default manager.
    dahl_objects = DahlBookManager() # The Dahl-specific manager.

इस नमूने के मॉडल के साथ, Book.objects.all() डेटाबेस में सभी पुस्तकें लौटा देगा, लेकिन Book.dahl_objects.all() केवल Roald Dahl द्वारा लिखित लोगों को वापस कर देगा।

बेशक, क्योंकि QuerySet get_queryset() एक QuerySet ऑब्जेक्ट लौटाता है, आप filter() , exclude() और उस पर अन्य सभी QuerySet विधियों का उपयोग कर सकते हैं। तो ये कथन सभी कानूनी हैं:

Book.dahl_objects.all()
Book.dahl_objects.filter(title='Matilda')
Book.dahl_objects.count()

इस उदाहरण ने एक और दिलचस्प तकनीक को भी इंगित किया: एक ही मॉडल पर कई प्रबंधकों का उपयोग करना। आप एक मॉडल में जितने चाहें उतने 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):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    role = models.CharField(max_length=1, choices=(('A', _('Author')), ('E', _('Editor'))))
    people = models.Manager()
    authors = AuthorManager()
    editors = EditorManager()

यह उदाहरण आपको Person.authors.all() , Person.editors.all() , और Person.people.all() , पूर्वानुमानित परिणाम देने का अनुरोध करने की अनुमति देता है।

डिफ़ॉल्ट प्रबंधक

Model._default_manager

यदि आप कस्टम Manager ऑब्जेक्ट का उपयोग करते हैं, तो ध्यान दें कि पहले Manager Django का सामना होता है (जिस क्रम में वे मॉडल में परिभाषित हैं) में एक विशेष स्थिति है। Django वर्ग में परिभाषित पहले Manager को "डिफ़ॉल्ट" Manager रूप में व्याख्या करता है, और Django के कई भाग ( dumpdata सहित) उस Manager उपयोग उस मॉडल के लिए विशेष रूप से करेंगे। परिणामस्वरूप, डिफ़ॉल्ट प्रबंधक की अपनी पसंद में सावधानी बरतने के लिए ऐसी स्थिति से बचने के लिए एक अच्छा विचार है जहां उन स्थितियों से बचने के लिए जो आपके साथ काम करना चाहते हैं, उन वस्तुओं को पुनः प्राप्त करने की अक्षमता के परिणामस्वरूप।

आप Meta.default_manager_name का उपयोग करके एक कस्टम डिफ़ॉल्ट प्रबंधक निर्दिष्ट कर सकते हैं।

यदि आप कुछ कोड लिख रहे हैं जो एक अज्ञात मॉडल को संभालना चाहिए, उदाहरण के लिए, एक तृतीय-पक्ष ऐप में जो एक सामान्य दृश्य को लागू करता है, तो मॉडल का objects प्रबंधक होने के बजाय इस प्रबंधक (या _base_manager ) का उपयोग करें।

आधार प्रबंधक

Model._base_manager

यदि सामान्य आधार प्रबंधक वर्ग ( django.db.models.Manager ) आपकी परिस्थितियों के लिए उपयुक्त नहीं है, तो आप Django को बता सकते हैं कि Meta.base_manager_name सेट करके किस वर्ग का उपयोग करना है।

संबंधित मॉडल पर क्वेरी करते समय आधार प्रबंधकों का उपयोग नहीं किया जाता है। उदाहरण के लिए, यदि ट्यूटोरियल के Question मॉडल में एक deleted फ़ील्ड और एक आधार प्रबंधक होता है जो deleted=True साथ इंस्टेंस को फ़िल्टर करता है, तो Choice.objects.filter(question__name__startswith='What') तरह एक Choice.objects.filter(question__name__startswith='What')

इस प्रकार के प्रबंधक उपवर्ग में किसी भी परिणाम को फ़िल्टर न करें

इस प्रबंधक का उपयोग उन वस्तुओं तक पहुंचने के लिए किया जाता है जो किसी अन्य मॉडल से संबंधित हैं। उन स्थितियों में, Django को उस मॉडल के लिए सभी ऑब्जेक्ट्स देखने में सक्षम होना पड़ता है, जिससे उसे कुछ भी प्राप्त होता है, जिसे पुनर्प्राप्त किया जा सकता है।

यदि आप get_queryset() विधि को ओवरराइड करते हैं और किसी भी पंक्तियों को फ़िल्टर करते हैं, तो Django गलत परिणाम देगा। ऐसा मत करो। एक प्रबंधक जो फ़िल्टर का परिणाम get_queryset() है, आधार प्रबंधक के रूप में उपयोग के लिए उपयुक्त नहीं है।

प्रबंधक से कस्टम QuerySet विधि को कॉल करना

जबकि मानक QuerySet अधिकांश तरीके सीधे Manager से सुलभ हैं, यह केवल कस्टम QuerySet पर परिभाषित अतिरिक्त विधियों के लिए है यदि आप उन्हें Manager पर भी लागू करते हैं:

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

    def editors(self):
        return self.filter(role='E')

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

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

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

class Person(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    role = models.CharField(max_length=1, choices=(('A', _('Author')), ('E', _('Editor'))))
    people = PersonManager()

यह उदाहरण आपको प्रबंधक Person.people से सीधे दोनों authors() और editors() को कॉल करने की अनुमति देता है।

QuerySet विधियों के साथ एक प्रबंधक बनाना

उपरोक्त दृष्टिकोण के एवज में, जिसे QuerySet और Manager दोनों पर दोहराए जाने वाले तरीकों की आवश्यकता होती है, QuerySet.as_manager() का उपयोग कस्टम QuerySet की विधियों की प्रतिलिपि के साथ Manager का एक उदाहरण बनाने के लिए किया जा सकता है:

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

QuerySet.as_manager() द्वारा बनाया गया Manager उदाहरण वस्तुतः पिछले उदाहरण से PersonManager Manager समान होगा।

प्रत्येक QuerySet विधि Manager स्तर पर समझ में नहीं QuerySet है; उदाहरण के लिए, हम जानबूझकर QuerySet.delete() विधि को Manager वर्ग पर कॉपी होने से QuerySet.delete()

निम्नलिखित नियमों के अनुसार तरीकों की नकल की जाती है:

  • सार्वजनिक तरीके डिफ़ॉल्ट रूप से कॉपी किए जाते हैं।
  • निजी तरीकों (एक अंडरस्कोर के साथ शुरू) को डिफ़ॉल्ट रूप से कॉपी नहीं किया जाता है।
  • False तरीके से सेट किए गए queryset_only विशेषता वाले तरीके हमेशा कॉपी किए जाते हैं।
  • True सेट queryset_only विशेषता वाले तरीके कभी भी कॉपी नहीं किए जाते हैं।

उदाहरण के लिए:

class CustomQuerySet(models.QuerySet):
    # Available on both Manager and QuerySet.
    def public_method(self):
        return

    # Available only on QuerySet.
    def _private_method(self):
        return

    # Available only on QuerySet.
    def opted_out_public_method(self):
        return
    opted_out_public_method.queryset_only = True

    # Available on both Manager and QuerySet.
    def _opted_in_private_method(self):
        return
    _opted_in_private_method.queryset_only = False

from_queryset()

classmethod from_queryset(queryset_class)

उन्नत उपयोग के लिए आप एक कस्टम Manager और एक कस्टम QuerySet दोनों चाह सकते हैं। आप ऐसा कर सकते हैं जो Manager.from_queryset() को कॉल करके आपके आधार Manager उप-वर्ग को कस्टम QuerySet विधियों की एक प्रति के साथ QuerySet है:

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

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

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

आप जनरेट किए गए वर्ग को एक चर में स्टोर कर सकते हैं:

CustomManager = BaseManager.from_queryset(CustomQuerySet)

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

कस्टम प्रबंधक और मॉडल वंशानुक्रम

यहाँ बताया गया है कि कैसे Django कस्टम प्रबंधकों और मॉडल वंशानुक्रम को संभालता है:

  1. बेस कक्षाओं के प्रबंधकों को हमेशा चाइल्ड क्लास द्वारा विरासत में दिया जाता है, पायथन के सामान्य नाम रिज़ॉल्यूशन ऑर्डर (चाइल्ड क्लास पर नाम अन्य सभी को ओवरराइड करते हैं; फिर पहले पैरेंट क्लास पर नाम आते हैं, इत्यादि)।
  2. यदि कोई प्रबंधक किसी मॉडल और / या उसके माता-पिता पर घोषित नहीं किया जाता है, तो Django स्वचालित रूप से objects प्रबंधक बनाता है।
  3. किसी वर्ग पर डिफ़ॉल्ट प्रबंधक या तो Meta.default_manager_name साथ चुना जाता है, या मॉडल पर घोषित पहला प्रबंधक, या पहले मूल मॉडल का डिफ़ॉल्ट प्रबंधक होता है।

ये नियम आवश्यक लचीलापन प्रदान करते हैं यदि आप एक सार आधार वर्ग के माध्यम से मॉडल के समूह पर कस्टम प्रबंधकों का एक संग्रह स्थापित करना चाहते हैं, लेकिन फिर भी डिफ़ॉल्ट प्रबंधक को अनुकूलित करें। उदाहरण के लिए, मान लें कि आपके पास यह आधार वर्ग है:

class AbstractBase(models.Model):
    # ...
    objects = CustomManager()

    class Meta:
        abstract = True

यदि आप सीधे उपवर्ग में इसका उपयोग करते हैं, तो आधार वर्ग में कोई प्रबंधक घोषित न करने पर objects डिफ़ॉल्ट प्रबंधक होंगी:

class ChildA(AbstractBase):
    # ...
    # This class has CustomManager as the default manager.
    pass

यदि आप AbstractBase से वारिस करना चाहते हैं, लेकिन एक अलग डिफ़ॉल्ट प्रबंधक प्रदान करते हैं, तो आप बच्चे के वर्ग पर डिफ़ॉल्ट प्रबंधक प्रदान कर सकते हैं:

class ChildB(AbstractBase):
    # ...
    # An explicit default manager.
    default_manager = OtherManager()

यहां, default_manager डिफ़ॉल्ट है। objects मैनेजर अभी भी उपलब्ध है, क्योंकि यह विरासत में मिला है। यह सिर्फ डिफ़ॉल्ट के रूप में उपयोग नहीं किया जाता है।

अंत में इस उदाहरण के लिए, मान लीजिए कि आप अतिरिक्त प्रबंधकों को चाइल्ड क्लास में जोड़ना चाहते हैं, लेकिन फिर भी AbstractBase से डिफ़ॉल्ट का उपयोग करें। आप सीधे नए प्रबंधक को चाइल्ड क्लास में नहीं जोड़ सकते, क्योंकि यह डिफ़ॉल्ट को ओवरराइड करेगा और आपको एब्सट्रैक्ट बेस क्लास के सभी मैनेजरों को भी स्पष्ट रूप से शामिल करना होगा। इसका उपाय यह है कि अतिरिक्त प्रबंधकों को एक अन्य बेस क्लास में रखा जाए और चूक के बाद विरासत वंशानुक्रम में पेश किया जाए:

class ExtraManager(models.Model):
    extra_manager = OtherManager()

    class Meta:
        abstract = True

class ChildC(AbstractBase, ExtraManager):
    # ...
    # Default manager is CustomManager, but OtherManager is
    # also available via the "extra_manager" attribute.
    pass

ध्यान दें कि जब आप अमूर्त मॉडल पर एक कस्टम प्रबंधक को परिभाषित कर सकते हैं , तो आप सार मॉडल का उपयोग करके किसी भी तरीके को लागू नहीं कर सकते। अर्थात्:

ClassA.objects.do_something()

कानूनी है, लेकिन:

AbstractBase.objects.do_something()

एक अपवाद बढ़ा देंगे। ऐसा इसलिए है क्योंकि प्रबंधकों का उद्देश्य वस्तुओं के संग्रह के प्रबंधन के लिए तर्क को बदलना है। चूंकि आपके पास अमूर्त वस्तुओं का संग्रह नहीं हो सकता है, इसलिए उन्हें प्रबंधित करने का कोई मतलब नहीं है। यदि आपके पास कार्यक्षमता है जो अमूर्त मॉडल पर लागू होती है, तो आपको उस कार्यक्षमता को अमूर्त मॉडल पर एक staticmethod या classmethod में रखना चाहिए।

कार्यान्वयन की चिंता

आप अपने कस्टम Manager जो भी सुविधाएँ जोड़ते हैं, वह Manager उदाहरण की उथली प्रतिलिपि बनाना संभव होगा; यानी, निम्न कोड काम करना चाहिए:

>>> import copy
>>> manager = MyManager()
>>> my_copy = copy.copy(manager)

Django कुछ प्रश्नों के दौरान प्रबंधक वस्तुओं की उथली प्रतियां बनाता है; यदि आपका प्रबंधक कॉपी नहीं किया जा सकता है, तो वे प्रश्न विफल हो जाएंगे।

यह अधिकांश कस्टम प्रबंधकों के लिए कोई समस्या नहीं होगी। यदि आप अपने Manager लिए सिर्फ सरल तरीके जोड़ रहे हैं, तो यह संभव नहीं है कि आप अनजाने में अपने Manager उदाहरणों को बिना सोचे समझे बना लेंगे। हालाँकि, यदि आप __getattr__ या आपके Manager ऑब्जेक्ट के कुछ अन्य निजी तरीके को ओवरराइड कर रहे हैं, जो ऑब्जेक्ट स्थिति को नियंत्रित करता है, तो आपको यह सुनिश्चित करना चाहिए कि आप अपने Manager की प्रतिलिपि बनाने की क्षमता को प्रभावित न करें।