2011-06-10 7 views
7

इस तरह विरासत में मिला मॉडल कक्षाओं का एक सरल सेट, मान लें:Django में मॉडल बेस से व्युत्पन्न कक्षा में कैसे जाना है?

class BaseObject(models.Model): 
    some_field = models.SomeField(...) 

class AwesomeObject(BaseObject): 
    awesome_field = models.AwesomeField(...) 

class ExcellentObject(BaseObject): 
    excellent_field = models.ExcellentField(...) 

और एक प्रश्न है कि इस तरह दिखता है:

found_objects = BaseObject.objects.filter(some_field='bogus') 

प्रत्येक found वस्तु लेने के लिए और इसे फिर से चालू में करने के लिए सबसे अच्छा तरीका क्या है यह व्युत्पन्न कक्षा है? मैं जिस कोड का उपयोग कर रहा हूं वह इस प्रकार है:

for found in found_objects: 
    if hasattr(found, 'awesomeobject'): 
     ProcessAwesome(found.awesomeobject) 
    elif hasattr(found, 'excellentobject'): 
     ProcessExcellent(found.excellentobject): 

लेकिन, ऐसा लगता है कि यह "हैशर" का दुरुपयोग है। क्या बेस क्लास पर एक स्पष्ट "प्रकार" फ़ील्ड बनाये बिना ऐसा करने का कोई बेहतर तरीका है?

उत्तर

2

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

मैं माता-पिता पर एक विधि बनाने की कोशिश करता हूं जैसे कि get_child, जो केवल विशेषताओं के माध्यम से चक्र और एक है कि पॉप रिटर्न:

class BaseObject(models.Model): 
    some_field = models.SomeField(...) 

    def get_child(self): 
     if hasattr(self, 'awesomeobject'): 
      return ProcessAwesome(found.awesomeobject) 
     elif hasattr(self, 'excellentobject'): 
      return ProcessExcellent(found.excellentobject): 
     else: 
      return None 

कम से कम तो, तुम बस found.get_child() कॉल कर सकते हैं, और हो सकता है कि आप hackery वहाँ हो जाता है के बारे में भूल जाते हैं।

+1

नाह .. django-polymorphic पर बेहतर दिखें ;-) – vdboor

2

मैं आत्मनिरीक्षण का उपयोग करता हूं;

class Base(models.Model): 
[ we have some unique 'key' attribute ] 
class_name = models.CharField(..., editable=False) 

def get_base(self): 
    if self.__class__ == Base: 
     return self 
    # if we are not an instance of Base we 'go up' 
    return Base.objects.get(key=self.key) 

def get_specific(self): 
    if self.__class__ != Base: 
     return self 
    # if we are an instance of Base we find the specific class 
    class_type = getattr(sys.modules["project.app.models"], 
     self.class_name) 
    return class_type.objects.get(key=self.key) 

आप विशिष्ट वर्गों बनाने के लिए कुछ कारखाने की जरूरत है ताकि आप सही तरीके से CLASS_NAME

+0

हाँ, self.class_name संग्रहित करना मैं क्या टालने की कोशिश कर रहा हूं ... – slacy

3

एक आधार वर्ग से एक व्युत्पन्न वर्ग के लिए जा रहे में str (आत्म। वर्ग) बचाने के लिए सुनिश्चित कर रहे हैं आम तौर पर एक संकेत है एक कार्यक्रम में खराब डिजाइन का। hasattr का उपयोग करके आप जिस विधि का प्रस्ताव देते हैं, वह गंभीर समस्या हो सकती है। मैं तुम्हें दिखाता हूँ:

# defined in some open source library 
class MyObject(object): 
    def what_is_derived(self): 
     if hasattr(self, 'derived1'): 
      return 'derived1' 
     elif hasattr(self, 'derived2'): 
      return 'derived2' 
     else: 
      return 'base' 

के लिए बहाना है कि कक्षाओं Derived1 और Derived2 है कि एक ही पुस्तकालय में परिभाषित कर रहे हैं। अब, आप MyObject की विशेषताओं का उपयोग करना चाहते हैं, ताकि आप इसे अपने कोड में प्राप्त कर सकें।

# defined in your own code 
class MyBetterObject(MyObject): 
    pass 

better_object = MyBetterObject() 
better_object.what_is_derived() # prints 'base' 

बहुरूपता का पूरा बिंदु यह है कि आप बेस क्लास को बदलने के बिना कई व्युत्पन्न कक्षाएं प्राप्त कर सकते हैं। आधार वर्ग को इसके सभी व्युत्पन्न वर्गों के बारे में जागरूक करके, आप इस तरह के वर्ग की उपयोगिता को गंभीर रूप से कम करते हैं। आप बेस क्लास को बदले बिना व्युत्पन्न कक्षा नहीं बना सकते हैं।

या तो आप एक व्युत्पन्न कक्षा के साथ काम करना चाहते हैं, या आपको कोई परवाह नहीं है कि विशिष्ट वर्ग क्या है और आपको केवल बेस क्लास के गुण/विधियां हैं। यह सभी ओओपी भाषाओं में समान है। व्युत्पन्न वर्ग क्या है, यह जानने के लिए सुविधाएं हैं, लेकिन आमतौर पर यह एक बुरा विचार है।

एक Django मॉडल दृष्टिकोण से, मैं आमतौर पर इस तरह से विरासत का उपयोग करें:

class Address(models.Model): 
    # fields... 

class Person(Address): 
    # fields... 

class Business(Address): 
    # fields... 

Address.objects.all() # find all addresses for whatever reason 
Person.objects.all() # im only interested in people 
Business.objects.all() # need to work with businesses 

# need to show all addresses in a postcode, and what type of address they are? 
businesses = Business.objects.filter(postcode='90210') 
people = Person.objects.filter(postcode='90210') 
# use the address properties on both 

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

+1

"से जा रहा है एक व्युत्पन्न वर्ग के लिए एक बेस क्लास आमतौर पर एक कार्यक्रम में खराब डिजाइन का संकेत है। " क्या बकवास। बेस क्लास से व्युत्पन्न कक्षा में जाकर पॉलिमॉर्फिज्म कहा जाता है और ऑब्जेक्ट-ओरिएंटेशन के लिए मौलिक है। यह सिर्फ इतना है कि इस विशिष्ट मामले में यह हैक के बिना काम नहीं करता है। – jwg

+1

@jwg क्या आपने बाकी लिखा है, या केवल उस विशेष वाक्य पर ध्यान केंद्रित किया है? पॉलिमॉर्फिज्म तब काम करता है जब आपके पास पेड़ में आम तौर पर विधियों/गुण होते हैं। यदि आपको व्युत्पन्न विशेषताओं की आवश्यकता है, तो आपको मूल मॉडल के रूप में व्युत्पन्न मॉडल के साथ काम करना चाहिए। Django बहु-टेबल मॉडल विरासत polymorphism का एक गरीब अनुमान है, और मेरे विचार में, (लगभग) सभी django विरासत सार आधार के साथ किया जाना चाहिए। –

+1

और .. बस इतना स्पष्ट है। "बेस क्लास से व्युत्पन्न कक्षा में जाना आमतौर पर एक कार्यक्रम में खराब डिजाइन का संकेत है।" - जब एक व्युत्पन्न वर्ग के लिए downcasting। उप-वर्ग विधि या विशेषता को गतिशील रूप से प्रेषित करने के लिए बहुरूपता का उपयोग करना ठीक है। –

4

इस विशिष्ट समस्या के लिए, django-polymorphic है। यह डीजेगो में सामग्री प्रकार ढांचे का उपयोग करके मॉडल आईडी को संग्रहीत करने के लिए काम करता है जो व्युत्पन्न तालिका इंगित करता है। जब आप क्वेरीसेट का मूल्यांकन करते हैं, तो यह सभी मॉडलों को उनके विशिष्ट प्रकार को उजागर करेगा।

आप मिल जाएगा:

>>> BaseProject.objects.all() 
[ <AwesomeObject>, <ExcellentObject>, <BaseObject>, <AwesomeObject> ] 
+0

ठीक है मैं देख सकता हूं कि आप व्युत्पन्न-वर्ग उदाहरण प्राप्त करने के लिए प्रबंधक विधियों का उपयोग कर सकते हैं। अभिभावक मॉडल उदाहरण से व्युत्पन्न मॉडल उदाहरण में जाने के लेखक के प्रश्न के बारे में कैसे? क्या django-polymorphic इसके लिए भी एक समाधान प्रदान करता है? – eugene

+1

हां यह करता है, व्यक्तिगत मॉडल में 'get_real_instance()' विधि भी होती है। अधिकांश समय आपको इसकी आवश्यकता नहीं होती है, क्योंकि प्रबंधक पहले ही व्युत्पन्न मॉडल देता है। इसके अलावा, यह एक ही प्रश्न में एक ही प्रकार की एकाधिक वस्तुओं को लाकर एन-क्वेरी समस्या से बचाता है। – vdboor

0

तुम भी InheritanceQuerySetdjango-model-utils से उपयोग कर सकते हैं मामले में आप स्पष्ट रूप से राज्य के लिए जो प्रभावित करने के लिए प्रश्नों, इस तरह हैं:

from model_utils.managers import InheritanceQuerySet 

class UserManager([...]): 

    def get_queryset(self): 
     return InheritanceQuerySet(self.model).select_subclasses() 

(https://stackoverflow.com/a/25108201 से कोड)

संबंधित मुद्दे