मैं चाइल्ड क्लास के नाम को जाने बिना django में किसी ऑब्जेक्ट के चाइल्ड क्लासेस को कैसे एक्सेस कर सकता हूँ?


81

Django में, जब आपके पास एक पैरेंट क्लास और कई चाइल्ड क्लासेस होते हैं, जो आपसे विरासत में लेते हैं, तो आप आमतौर पर parentclass.childclass1_set या parentclass.childclass2_set के माध्यम से एक बच्चे को एक्सेस करेंगे, लेकिन अगर मुझे विशिष्ट चाइल्ड क्लास का नाम नहीं पता है तो मुझे क्या चाहिए?

क्या माता-पिता से संबंधित वस्तुओं को प्राप्त करने का एक तरीका है- बच्चे की कक्षा का नाम जाने बिना बच्चे की दिशा?


79
@ S.Lott इस प्रकार की प्रतिक्रियाएं वास्तव में पुरानी हो जाती हैं। सिर्फ इसलिए कि आप एक उपयोग के मामले के बारे में सोच नहीं सकते इसका मतलब यह नहीं है कि पूछने वाले के पास एक नहीं है। यदि आप किसी भी प्रकार के बहुरूपी व्यवहार के लिए उपवर्ग का उपयोग कर रहे हैं (आप जानते हैं, OOP के प्राथमिक कथित लाभों में से एक?) यह प्रश्न एक बहुत ही स्वाभाविक और स्पष्ट आवश्यकता है।
कार्ल मेयर

82
@ S.Lott उस मामले में, कुछ नहीं-अशिष्ट संस्करणों का अभ्यास करने के लिए स्वतंत्र महसूस करें, जैसे कि "मुझे यकीन नहीं है कि मैं संदर्भ को समझता हूं। क्या आप अपने उपयोग के मामले की व्याख्या कर सकते हैं?"
कार्ल मेयर

जवाबों:


84

( अपडेट : Django 1.2 और नए के लिए, जो रिवर्स OneToOneField संबंधों (और इस प्रकार वंशानुगत पदानुक्रम) में select_related प्रश्नों का अनुसरण कर सकता है, वहां एक बेहतर तकनीक उपलब्ध है, जिसे real_typeमूल मॉडल पर जोड़े गए फ़ील्ड की आवश्यकता नहीं है । यह InheritanceManager के रूप में उपलब्ध है। django- मॉडल-बर्तन परियोजना।)

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

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 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 को सीधे अपने विशेष वंशानुक्रम पदानुक्रम में मूल वर्ग पर भी डाल सकते हैं।

यदि आप मूल मॉडल को संशोधित करने में सक्षम नहीं हैं तो यह समाधान काम नहीं करेगा। उस स्थिति में आप मैन्युअल रूप से सभी उपवर्गों की जाँच कर रहे हैं।


धन्यवाद। यह सुंदर है और निश्चित रूप से मुझे समय की बचत हुई है।
स्पाइक २

यह बहुत उपयोगी रहा है, लेकिन मुझे आश्चर्य है कि आप FK को null = True के साथ परिभाषित क्यों करना चाहते हैं। हमने कोड को अधिक-या-कम के रूप में कॉपी किया है, और एक बग द्वारा बिट किया गया है जो एफके अनिवार्य होने पर आसानी से पता लगाया और हल किया जाएगा (ध्यान दें कि कास्ट () विधि इसे अनिवार्य मानती है)।
Shai Berger

1
@ शाईबर्गर उत्कृष्ट प्रश्न। तीन + साल बाद, मुझे नहीं पता कि यह मूल रूप से ऐसा क्यों था :-) null = True हटाने के लिए संपादन।
कार्ल मेयर

2
@sunprophit आपको मेरी प्रतिक्रिया को और ध्यान से पढ़ने की आवश्यकता है। हां, निश्चित रूप से आप फ़ील्ड self.child_object()का उपयोग कर सकते हैं real_type(जो कि cast()विधि के रूप में ऊपर कोड में है), और यह एक उदाहरण के लिए केवल एक क्वेरी लेगा। लेकिन अगर आपके पास उदाहरणों से भरा क्वेरीसेट है, तो वह एन क्वेरीज़ बन जाता है। एक ही क्वेरी में वस्तुओं की एक पूरी क्वेरी के लिए उपवर्ग डेटा प्राप्त करने का एकमात्र तरीका है कि इनहेरिटैंसमैनेजर जोइन्स का उपयोग करता है।
कार्ल मेयर

1
मेरी गलती वास्तव में, मुझे शर्म आती है। इसे नोट करने और इसे ठीक करने के लिए धन्यवाद।
एंटोनी पिंसर्ड

23

पायथन में, एक ("नई शैली") कक्षा X दिया गया है, आप इसके (प्रत्यक्ष) उपवर्गों को प्राप्त कर सकते हैं X.__subclasses__(), जो वर्ग वस्तुओं की एक सूची देता है। (यदि आप "आगे के वंशज" चाहते हैं, तो आपको कॉल भी करना होगा__subclasses__ प्रत्येक प्रत्यक्ष उपवर्ग, आदि आदि पर - यदि आपको पायथन में प्रभावी ढंग से करने के लिए मदद की आवश्यकता है, तो बस पूछें!)।

एक बार जब आप किसी तरह से किसी बच्चे की रुचि के वर्ग की पहचान कर लेते हैं (हो सकता है कि उनमें से सभी, यदि आप सभी बच्चे उपवर्गों के उदाहरण चाहते हैं, आदि), getattr(parentclass,'%s_set' % childclass.__name__)तो मदद करनी चाहिए (यदि बच्चे की कक्षा का नाम है 'foo', तो यह एक्सेस करने जैसा है parentclass.foo_set- कोई और अधिक, कोई कम नहीं ) है। फिर, अगर आपको स्पष्टीकरण या उदाहरण चाहिए, तो कृपया पूछें!


1
यह बहुत अच्छी जानकारी है (मुझे उपवर्गों के बारे में पता नहीं था ), लेकिन मेरा मानना ​​है कि सवाल वास्तव में बहुत अधिक विशिष्ट है कि कैसे Django मॉडल विरासत को लागू करते हैं। यदि आप "पैरेंट" तालिका को क्वेरी करते हैं तो आपको माता-पिता का एक उदाहरण वापस मिल जाएगा। यह वास्तव में SomeChild का एक उदाहरण हो सकता है, लेकिन Django यह पता नहीं लगाता है कि आपके लिए स्वचालित रूप से (महंगा हो सकता है)। आप पेरेंट उदाहरण पर एक विशेषता के माध्यम से कुछ चाइल्डहिल इंस्टेंस पर जा सकते हैं, लेकिन केवल अगर आप पहले से ही जानते हैं कि यह कुछ है, जो आप चाहते हैं, तो पेरेंट के कुछ अन्य उपवर्गों के विपरीत।
कार्ल मेयर

2
क्षमा करें, यह स्पष्ट नहीं है जब मैं कहता हूं " वास्तव में SomeChild का एक उदाहरण हो सकता है।" आपके पास पाइथन में माता-पिता का एक उदाहरण है, लेकिन यह SomeChild तालिका में संबंधित प्रविष्टि हो सकती है, जिसका अर्थ है कि आप SomeChild उदाहरण के रूप में इसके साथ काम करना पसंद कर सकते हैं।
कार्ल मेयर

यह हास्यास्पद है ... मुझे उस समय इस विशिष्ट जानकारी की आवश्यकता नहीं थी, लेकिन मैं सिर्फ एक अलग मुद्दे के बारे में सोच रहा था और यह वही निकला जो मुझे चाहिए था, इसलिए फिर से धन्यवाद!
गेब्रियल हर्ले

यह वास्तविक जवाब है। Django दिन के अंत में सिर्फ पायथन है।
सांपनब्रोनियों

5

कार्ल का समाधान एक अच्छा है, यहाँ एक तरीका है इसे मैन्युअल रूप से करने के लिए यदि कई संबंधित बाल वर्ग हैं:

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 विकसित होता है, लेकिन यह चाल करता है और ज़रूरत पड़ने पर ऑन-द-फ्लाई का उपयोग किया जा सकता है।


5

यह पता चला है कि मुझे वास्तव में क्या चाहिए था:

सामग्री प्रकार और विरासत-जागरूक प्रबंधक के साथ मॉडल वंशानुक्रम

मेरे लिए पूरी तरह से काम किया है। हालांकि, बाकी सब के लिए धन्यवाद। मैंने आपके जवाबों को पढ़कर बहुत कुछ सीखा है!


5

आप उसके लिए django-polymorphic का उपयोग कर सकते हैं ।

यह स्वचालित रूप से व्युत्पन्न वर्गों को उनके वास्तविक प्रकार पर वापस लाने की अनुमति देता है। यह Django व्यवस्थापक समर्थन, अधिक कुशल SQL क्वेरी हैंडलिंग और प्रॉक्सी मॉडल, इनलाइन और फॉर्मेट समर्थन भी प्रदान करता है।

मूल सिद्धांत को कई बार पुनर्निवेशित किया गया है (वागटेल सहित .specific, या इस पोस्ट में उल्लिखित उदाहरण)। हालांकि, यह सुनिश्चित करने के लिए अधिक प्रयास होता है कि यह एन-क्वेरी मुद्दे का परिणाम नहीं देता है, या व्यवस्थापक, फ़ॉर्मेट / इनलाइन या थर्ड पार्टी ऐप के साथ अच्छी तरह से एकीकृत होता है।


1
यह अच्छा लग रहा है और मैं इसे आजमाना चाहता हूं। जब माइग्रेट करना उचित पॉलीटाइप्स (एक दक्षिण प्रवास में) के साथ पॉलीमॉर्फ़िक_क्टाइप फ़ील्ड भरने के लिए पर्याप्त है?
जोशुआ

1
@ जोशुआ: हाँ, यह ठीक वही चीज़ है जो आपको करनी होगी।
vdboor

आप django-model-utils (कार्ल मेयर्स का उत्तर देखें) में लिए गए दृष्टिकोण से अंतर को समझाकर अपने उत्तर को थोड़ा बढ़ा सकते हैं।
रेन एवरेट

1
स्वीकृत उत्तर पुराना है, यह अब तक का सबसे अच्छा उत्तर है।
पियरे.सोउलास

2

यहां मेरा समाधान है, फिर से इसका उपयोग _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

0

आप इसे उन सभी क्षेत्रों के लिए खोज सकते हैं जो मूल में django.db.models.fields.related.elatedManager का उदाहरण हैं। आपके उदाहरण से ऐसा लगता है कि जिन बाल वर्गों की आप बात कर रहे हैं, वे उपवर्ग नहीं हैं। सही?


0

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

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.