سؤال

كنت أتساءل إذا كان من الممكن (و, إذا كان الأمر كذلك ، كيف) إلى سلسلة معا متعددة مديري لإنتاج مجموعة الاستعلام الذي يتأثر كل من مديري الفردية.سأشرح محددة على سبيل المثال أن تعمل على:

لدي عدة مجردة نموذج الطبقات التي تستخدم لتوفير صغيرة, وظائف محددة إلى نماذج أخرى.اثنين من هذه النماذج هي DeleteMixin و GlobalMixin.

على DeleteMixin يتم تعريفها على هذا النحو:

class DeleteMixin(models.Model):
    deleted = models.BooleanField(default=False)
    objects = DeleteManager()

    class Meta:
        abstract = True

    def delete(self):
        self.deleted = True
        self.save()

في الأساس فإنه يوفر الزائفة حذف (حذف العلم) في الواقع بدلا من حذف الكائن.

على GlobalMixin يتم تعريفها على هذا النحو:

class GlobalMixin(models.Model):
    is_global = models.BooleanField(default=True)

    objects = GlobalManager()

    class Meta:
        abstract = True

أنه يسمح لأي كائن أن تكون محددة إما العالمية كائن أو كائن (مثل القطاعين العام والخاص بلوق وظيفة).

كل من هؤلاء المديرين الخاصة التي تؤثر على queryset التي يتم إرجاعها.بلدي DeleteManager مرشحات queryset إلى العودة فقط النتائج التي المحذوفة العلم تعيين إلى False ، في حين GlobalManager مرشحات queryset إلى العودة فقط النتائج التي تم وضع علامة العالمية.هنا هو إعلان لكل من:

class DeleteManager(models.Manager):
    def get_query_set(self):
        return super(DeleteManager, self).get_query_set().filter(deleted=False)

class GlobalManager(models.Manager):
    def globals(self):
        return self.get_query_set().filter(is_global=1)

الوظيفة المطلوبة أن يكون نموذج تمديد كل من هذه النماذج المجردة ومنح القدرة على العودة فقط النتائج التي هي على حد سواء من غير حذف و العالمية.ركضت اختبارا على نموذج مع 4 حالات:واحدة عالمية غير حذف أحد العالمية و حذف أحد غير العالمية وعدم حذف أحد غير عالمي و حذفها.إذا كنت في محاولة للحصول على مجموعات النتائج على هذا النحو:SomeModel.الكائنات.كل () ، على سبيل المثال 1 و 3 (اثنين من غير حذف - عظيم!).إذا حاولت SomeModel.الكائنات.globals () ، أنا على خطأ DeleteManager لا globals (هذا على افتراض بلدي نموذج الإعلان على هذا النحو:SomeModel(DeleteMixin, GlobalMixin).لو عكس الأمر, أنا لا تحصل على خطأ لكن هذا لا تصفية حذف منها).إذا قمت بتغيير GlobalMixin إرفاق GlobalManager إلى globals بدلا من الكائنات (حتى القيادة الجديدة سيكون SomeModel.globals.globals ()) ، أحصل على الحالات 1 و 2 (اثنين جلوبل) ، بينما المقصود النتيجة ستكون فقط على سبيل المثال 1 (عالمية وغير واحد المحذوفة).

لم أكن متأكدا إذا كان أي شخص قد واجهت أي حالة مشابهة لهذه وكان يأتي إلى نتيجة.أما طريقة لجعلها تعمل في التفكير الحالي أو إعادة العمل التي توفر وظائف أنا بعد سيكون محل تقدير كبير جدا.أعرف أن هذا المنصب كان طويل قليلا ينضب.إذا كان أي هناك حاجة إلى مزيد شرح, سأكون سعيدا لتقديم ذلك.

تحرير:

كنت قد نشرت في نهاية المطاف الحل اعتدت على هذه المشكلة المحددة أدناه.لأنه يقوم على الرابط سيمون مخصص QuerySetManager.

هل كانت مفيدة؟

المحلول

ترى هذا مقتطف على Djangosnippets: http://djangosnippets.org/snippets/734/

بدلا من وضع مخصص الأساليب في إدارة فرعية في queryset نفسها.فإنه من السهل جدا ويعمل تماما.المشكلة الوحيدة لدي هو مع نموذج الميراث ، عليك دائما أن تحدد مدير في نموذج فرعية (فقط:"كائنات = QuerySetManager()" في فئة فرعية) ، على الرغم من أنها سوف ترث queryset.هذا سوف يكون أكثر منطقية مرة كنت تستخدم QuerySetManager.

نصائح أخرى

هنا هو الحل لمشكلتي باستخدام مخصص QuerySetManager من قبل سيمون أن سكوت مرتبطة.

from django.db import models
from django.contrib import admin
from django.db.models.query import QuerySet
from django.core.exceptions import FieldError

class MixinManager(models.Manager):    
    def get_query_set(self):
        try:
            return self.model.MixinQuerySet(self.model).filter(deleted=False)
        except FieldError:
            return self.model.MixinQuerySet(self.model)

class BaseMixin(models.Model):
    admin = models.Manager()
    objects = MixinManager()

    class MixinQuerySet(QuerySet):

        def globals(self):
            try:
                return self.filter(is_global=True)
            except FieldError:
                return self.all()

    class Meta:
        abstract = True

class DeleteMixin(BaseMixin):
    deleted = models.BooleanField(default=False)

    class Meta:
        abstract = True

    def delete(self):
        self.deleted = True
        self.save()

class GlobalMixin(BaseMixin):
    is_global = models.BooleanField(default=True)

    class Meta:
        abstract = True

أي mixin في المستقبل الذي يريد لإضافة وظائف إضافية إلى مجموعة الاستعلام ببساطة يحتاج إلى تمديد BaseMixin (أو يكون ذلك في مكان ما في heirarchy).أي وقت أنا في محاولة تصفية الاستعلام المنصوص عليها أنا ملفوفة في محاولة الصيد في حال ميدان لا وجود له في الواقع (أي أنه لا يمتد هذا mixin).مرشح العالمية استدعاء باستخدام globals () ، في حين حذف الفلتر تلقائيا الاحتجاج به (إذا كان هناك شيء محذوف, أنا لا أريد أن تظهر).باستخدام هذا النظام يسمح الأنواع التالية من الأوامر:

TemporaryModel.objects.all() # If extending DeleteMixin, no deleted instances are returned
TemporaryModel.objects.all().globals() # Filter out the private instances (non-global)
TemporaryModel.objects.filter(...) # Ditto about excluding deleteds

شيء واحد هو أن نلاحظ أن حذف مرشح لن يؤثر على المشرف واجهات, لأن المدير الافتراضي أعلن الأولى (مما يجعل الافتراضي).أنا لا أتذكر عندما غيروا الادارة استخدام نموذج._default_manager بدلا من نموذج.الكائنات, ولكن أي حذف الحالات سوف لا تزال تظهر في المشرف (في حال كنت بحاجة إلى إلغاء حذفها).

قضيت بعض الوقت في محاولة الخروج مع وسيلة لبناء لطيفة مصنع للقيام بذلك ، ولكن أنا الوقوع في الكثير من المشاكل مع هذا.

أفضل ما يمكن أن توحي لك أن سلسلة الإرث الخاص بك.انها ليست عامة جدا, لذلك أنا لست متأكدا كم هو مفيد ، ولكن كل ما عليك فعله هو:

class GlobalMixin(DeleteMixin):
    is_global = models.BooleanField(default=True)

    objects = GlobalManager()

    class Meta:
        abstract = True

class GlobalManager(DeleteManager):
    def globals(self):
        return self.get_query_set().filter(is_global=1)

إذا كنت تريد شيئا أكثر عمومية ، فإن أفضل ما يمكن أن تأتي مع تحديد قاعدة Mixin و Manager أن يعيد get_query_set() (أفترض أنك فقط تريد أن تفعل هذا مرة واحدة ؛ الأمور معقدة جدا وإلا) ومن ثم تمرير قائمة الحقول التي تريد إضافتها عن طريق Mixins.

سوف ننظر بشيء من هذا القبيل (لم يختبر على الإطلاق):

class DeleteMixin(models.Model):
    deleted = models.BooleanField(default=False)

    class Meta:
        abstract = True

def create_mixin(base_mixin, **kwargs):
    class wrapper(base_mixin):
        class Meta:
            abstract = True
    for k in kwargs.keys():
        setattr(wrapper, k, kwargs[k])
    return wrapper

class DeleteManager(models.Manager):
    def get_query_set(self):
        return super(DeleteManager, self).get_query_set().filter(deleted=False)

def create_manager(base_manager, **kwargs):
    class wrapper(base_manager):
        pass
    for k in kwargs.keys():
        setattr(wrapper, k, kwargs[k])
    return wrapper

حسنا ، هذا هو قبيح ، ولكن ماذا فعل لك ؟ أساسا انها نفس الحل ولكن أكثر ديناميكية وأكثر قليلا الجافة ، وإن كان أكثر تعقيدا من قراءة.

أولا يمكنك إنشاء الخاصة بك مدير حيوي:

def globals(inst):
    return inst.get_query_set().filter(is_global=1)

GlobalDeleteManager = create_manager(DeleteManager, globals=globals)

وهذا يخلق المدير الجديد الذي هو فئة فرعية من DeleteManager ولها طريقة تسمى globals.

التالي, يمكنك إنشاء الخاصة بك mixin نموذج:

GlobalDeleteMixin = create_mixin(DeleteMixin,
                                 is_global=models.BooleanField(default=False),
                                 objects = GlobalDeleteManager())

كما قلت انها قبيحة.ولكن هذا يعني أنك لا تضطر إلى إعادة تعريف globals().إذا كنت ترغب في نوع مختلف من مدير إلى globals(), فقط اتصل create_manager مرة أخرى مع قاعدة مختلفة.و يمكنك إضافة العديد من الأساليب الجديدة كما تريد.نفسه على المدير فقط الحفاظ على إضافة وظائف جديدة من شأنها أن تعود مختلفة querysets.

هل هذا حقا عملي ؟ ربما لا.هذا الجواب هو أكثر من تمرين في (ab)باستخدام بايثون المرونة.لم أحاول استخدام هذه, على الرغم من أنني لا استخدم بعض مديري المدارس الأساسية حيوي توسيع الطبقات لجعل الأمور أسهل في الوصول إليها.

اسمحوا لي أن أعرف إذا كان أي شيء غير واضح و أنا تحديث الإجابة.

آخر خيار يستحق النظر هو PassThroughManager:

https://django-model-utils.readthedocs.org/en/latest/managers.html#passthroughmanager

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top