كيف يمكنني الوصول إلى فئات الأطفال الكائن في Django دون معرفة اسم فئة الطفل؟

StackOverflow https://stackoverflow.com/questions/929029

  •  06-09-2019
  •  | 
  •  

سؤال

في Django، عندما يكون لديك فصل من الوالدين وفصول الأطفال المتعددة التي ترثت منه، يمكنك عادة الوصول إلى طفل من خلال penterclass.childclass1_set أو pentriclass.childclass2_set، ولكن ماذا لو كنت أعرف اسم فئة الطفل المحددة التي أريدها؟

هل هناك طريقة للحصول على الكائنات ذات الصلة في اتجاه الوالدين> الطفل دون معرفة اسم فئة الطفل؟

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

المحلول

(تحديث: . real_type الحقل على النموذج الأم. انها متوفرة anheritancanager. في ال django-model-utils المشروع.)

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

from django.contrib.contenttypes.models import ContentType
from django.db import models

class InheritanceCastModel(models.Model):
    """
    An abstract base class that provides a ``real_type`` FK to ContentType.

    For use in trees of inherited models, to be able to downcast
    parent instances to their child types.

    """
    real_type = models.ForeignKey(ContentType, editable=False)

    def save(self, *args, **kwargs):
        if not self._state.adding:
            self.real_type = self._get_real_type()
        super(InheritanceCastModel, self).save(*args, **kwargs)

    def _get_real_type(self):
        return ContentType.objects.get_for_model(type(self))

    def cast(self):
        return self.real_type.get_object_for_this_type(pk=self.pk)

    class Meta:
        abstract = True

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

لن يعمل هذا الحل إذا لم تتمكن من تعديل النموذج الأصل. في هذه الحالة، تعثرت كثيرا على التحقق من جميع الفئات الفرعية يدويا.

نصائح أخرى

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

بمجرد أن تحدد بطريقة ما فئة طفولة من الفائدة (ربما كلها، إذا كنت تريد مثيلات لجميع الفئات الفرعية للأطفال، إلخ)، getattr(parentclass,'%s_set' % childclass.__name__) يجب أن تساعد (إذا كان اسم فئة الطفل 'foo', هذا هو تماما مثل الوصول parentclass.foo_set -- لا أكثر ولا أقل). مرة أخرى، إذا كنت بحاجة إلى توضيح أو أمثلة، يرجى السؤال!

حل Carl's هو فكرة جيدة، إليك طريقة واحدة للقيام بذلك يدويا إذا كانت هناك فئات أطفال متعددة ذات صلة:

def get_children(self):
    rel_objs = self._meta.get_all_related_objects()
    return [getattr(self, x.get_accessor_name()) for x in rel_objs if x.model != type(self)]

يستخدم وظيفة خارج _meta، والتي لا تكون مضمونة لتكون مستقرة مثل Django تتطور، ولكنها تفعل الخدعة ويمكن استخدامها على ذبابة إذا لزم الأمر.

اتضح أن ما احتاجته حقا كان هذا:

نموذج الميراث مع نوع المحتوى والميراث مدير

وقد عملت تماما بالنسبة لي. شكرا لكل شخص آخر. تعلمت الكثير فقط قراءة إجاباتك!

يمكنك استخدام Django-polymorphic. من أجل هذا.

يسمح بإلقاء الفئات المشتقة تلقائيا إلى النوع الفعلي. كما يوفر دعم مسؤول DJANGO، أكثر كفاءة مناولة SQL SQL، ونموذج وكيل، ودعم متنقل و Formset.

يبدو أن المبدأ الأساسي اخترع عدة مرات (بما في ذلك ذك .specific, أو الأمثلة الموضحة في هذا المنصب). ومع ذلك، يتطلب الأمر أكثر جهدا، للتأكد من أنه لا ينتج عنه مشكلة في استعلام N، أو دمج بشكل جيد مع تطبيقات المشرف أو النضلات / الموجية أو تطبيقات الطرف الثالث.

ها هو الحل الخاص بي، مرة أخرى يستخدم _meta لذلك غير مضمون لتكون مستقرة.

class Animal(models.model):
    name = models.CharField()
    number_legs = models.IntegerField()
    ...

    def get_child_animal(self):
        child_animal = None
        for r in self._meta.get_all_related_objects():
            if r.field.name == 'animal_ptr':
                child_animal = getattr(self, r.get_accessor_name())
        if not child_animal:
            raise Exception("No subclass, you shouldn't create Animals directly")
        return child_animal

class Dog(Animal):
    ...

for a in Animal.objects.all():
    a.get_child_animal() # returns the dog (or whatever) instance

يمكنك تحقيق ذلك في البحث عن جميع الحقول الموجودة في الوالد مثيل django.db.models.fields.related.relatedmentmentmanager. من مثالك، يبدو أن فئات الأطفال التي تتحدث عنها ليست فئة فرعية. حق؟

يمكن العثور على نهج بديل باستخدام الوكلاء في هذه المدونة post.. وبعد مثل الحلول الأخرى، لديها فوائدها والخصوم، والتي يتم وضعها بشكل جيد للغاية في نهاية المنشور.

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