2011-10-28 31 views
130

मैं बहु-किरायेदार एप्लिकेशन पर काम कर रहा हूं जिसमें कुछ उपयोगकर्ता फॉर्म में अतिरिक्त डेटा एकत्र करने और डेटा पर रिपोर्ट एकत्र करने के लिए अपने डेटा फ़ील्ड (व्यवस्थापक के माध्यम से) को परिभाषित कर सकते हैं। उत्तरार्द्ध बिट JSONField नहीं एक बढ़िया विकल्प बनाता है, इसलिए बजाय मैं निम्नलिखित समाधान है:Django गतिशील मॉडल फ़ील्ड

class CustomDataField(models.Model): 
    """ 
    Abstract specification for arbitrary data fields. 
    Not used for holding data itself, but metadata about the fields. 
    """ 
    site = models.ForeignKey(Site, default=settings.SITE_ID) 
    name = models.CharField(max_length=64) 

    class Meta: 
     abstract = True 

class CustomDataValue(models.Model): 
    """ 
    Abstract specification for arbitrary data. 
    """ 
    value = models.CharField(max_length=1024) 

    class Meta: 
     abstract = True 

नोट कैसे CustomDataField साइट के लिए एक ForeignKey है - प्रत्येक साइट कस्टम डेटा क्षेत्रों का एक अलग सेट करना होगा, लेकिन एक ही उपयोग करें डेटाबेस। फिर विभिन्न ठोस डेटा फ़ील्ड परिभाषित किया जा सकता है:

class UserCustomDataField(CustomDataField): 
    pass 

class UserCustomDataValue(CustomDataValue): 
    custom_field = models.ForeignKey(UserCustomDataField) 
    user = models.ForeignKey(User, related_name='custom_data') 

    class Meta: 
     unique_together=(('user','custom_field'),) 

यह निम्न उपयोग की ओर जाता है:

custom_field = UserCustomDataField.objects.create(name='zodiac', site=my_site) #probably created in the admin 
user = User.objects.create(username='foo') 
user_sign = UserCustomDataValue(custom_field=custom_field, user=user, data='Libra') 
user.custom_data.add(user_sign) #actually, what does this even do? 

लेकिन यह बहुत भद्दा लगता है, विशेष रूप से और मैन्युअल रूप से संबंधित डेटा बनाने के लिए जरूरत के साथ इसे ठोस मॉडल के साथ संबद्ध करें। क्या कोई बेहतर दृष्टिकोण है?

विकल्प है कि पूर्व emptively निकाल दिया गया:

  • कस्टम एसक्यूएल ऑन-द-मक्खी तालिकाओं को संशोधित करने के। आंशिक रूप से क्योंकि यह स्केल नहीं करेगा और आंशिक रूप से क्योंकि यह एक हैक का बहुत अधिक है।
  • स्कीमा-कम समाधान जैसे NoSQL। मेरे पास उनके खिलाफ कुछ भी नहीं है, लेकिन वे अभी भी एक अच्छे फिट नहीं हैं। आखिरकार यह डेटा टाइप किया गया है, और संभावना तीसरे पक्ष के रिपोर्टिंग एप्लिकेशन का उपयोग करने की संभावना है।
  • JSONField, ऊपर सूचीबद्ध के रूप में, क्योंकि यह प्रश्नों के साथ अच्छी तरह से काम नहीं करेगा।
+6

पूर्व emptively, इस इन सवालों के किसी भी नहीं है: http://stackoverflow.com/questions/7801729/django-model-with-dynamic-attributes http://stackoverflow.com/questions/ 2854656/प्रति-उदाहरण-गतिशील क्षेत्रों-Django मॉडल – GDorn

उत्तर

242

आज के रूप में, वहाँ चार उपलब्ध दृष्टिकोण, उनमें से दो एक निश्चित भंडारण बैकएंड की आवश्यकता होती है:

  1. Django-eav (मूल पैकेज नहीं रह गया है mantained है लेकिन कुछ है thriving forks)

    यह समाधानपर आधारित हैडेटा मॉडल, अनिवार्य रूप से, यह वस्तुओं के गतिशील विशेषताओं को संग्रहीत करने के लिए कई तालिकाओं का उपयोग करता है।

    • कई शुद्ध और सरलता Django मॉडल का उपयोग करता है जो यह समझने के लिए सरल और डेटाबेस-नास्तिक बनाता गतिशील क्षेत्रों का प्रतिनिधित्व करने,;: इस समाधान के बारे में महान भागों है कि यह है
    • आप प्रभावी रूप से देते हैं/की तरह साधारण आदेशों के साथ Django मॉडल के लिए गतिशील विशेषता भंडारण अलग करने की अनुमति देता है:

      eav.unregister(Encounter) 
      eav.register(Patient) 
      
    • Nicely integrates with Django admin;

    • एक ही समय में वास्तव में शक्तिशाली होने के नाते।

    Downsides:

      नहीं
    • बहुत ही कुशल। यह ईएवी पैटर्न की आलोचना से अधिक है, जिसके लिए मॉडल में कुंजी-मूल्य जोड़े के सेट पर कॉलम प्रारूप से डेटा को मैन्युअल रूप से विलय करने की आवश्यकता होती है।
    • बनाए रखने के लिए कठिन। डेटा अखंडता को बनाए रखने के लिए एक बहु-स्तंभ अद्वितीय कुंजी बाधा की आवश्यकता होती है, जो कुछ डेटाबेस पर अक्षम हो सकती है।
    • आपको one of the forks चुनने की आवश्यकता होगी, क्योंकि आधिकारिक पैकेज अब बनाए रखा नहीं गया है और कोई स्पष्ट नेता नहीं है।

    उपयोग बिल्कुल स्पष्ट है:

    import eav 
    from app.models import Patient, Encounter 
    
    eav.register(Encounter) 
    eav.register(Patient) 
    Attribute.objects.create(name='age', datatype=Attribute.TYPE_INT) 
    Attribute.objects.create(name='height', datatype=Attribute.TYPE_FLOAT) 
    Attribute.objects.create(name='weight', datatype=Attribute.TYPE_FLOAT) 
    Attribute.objects.create(name='city', datatype=Attribute.TYPE_TEXT) 
    Attribute.objects.create(name='country', datatype=Attribute.TYPE_TEXT) 
    
    self.yes = EnumValue.objects.create(value='yes') 
    self.no = EnumValue.objects.create(value='no') 
    self.unkown = EnumValue.objects.create(value='unkown') 
    ynu = EnumGroup.objects.create(name='Yes/No/Unknown') 
    ynu.enums.add(self.yes) 
    ynu.enums.add(self.no) 
    ynu.enums.add(self.unkown) 
    
    Attribute.objects.create(name='fever', datatype=Attribute.TYPE_ENUM,\ 
                 enum_group=ynu) 
    
    # When you register a model within EAV, 
    # you can access all of EAV attributes: 
    
    Patient.objects.create(name='Bob', eav__age=12, 
              eav__fever=no, eav__city='New York', 
              eav__country='USA') 
    # You can filter queries based on their EAV fields: 
    
    query1 = Patient.objects.filter(Q(eav__city__contains='Y')) 
    query2 = Q(eav__city__contains='Y') | Q(eav__fever=no) 
    
  2. Hstore, JSON या JSONB PostgreSQL

    PostgreSQL में फ़ील्ड्स कई और अधिक जटिल डेटा प्रकार का समर्थन करता है। अधिकांश तीसरे पक्ष के पैकेज के माध्यम से समर्थित हैं, लेकिन हाल के वर्षों में Django ने उन्हें django.contrib.postgres.fields में अपनाया है।

    HStoreField:

    Django-hstore मूल रूप से एक तीसरे पक्ष के पैकेज था, लेकिन Django 1.8 के रूप में HStoreField जोड़ा निर्मित एक, कई अन्य PostgreSQL समर्थित फ़ील्ड प्रकार के साथ।

    यह दृष्टिकोण इस अर्थ में अच्छा है कि यह आपको दोनों दुनिया के सर्वश्रेष्ठ: गतिशील फ़ील्ड और रिलेशनल डेटाबेस प्रदान करने देता है। हालांकि, हैस्टोर not ideal performance-wise है, खासकर यदि आप एक क्षेत्र में हजारों वस्तुओं को संग्रहित करने जा रहे हैं। यह केवल मूल्यों के लिए तारों का समर्थन करता है।

    #app/models.py 
    from django.contrib.postgres.fields import HStoreField 
    class Something(models.Model): 
        name = models.CharField(max_length=32) 
        data = models.HStoreField(db_index=True) 
    

    Django के खोल में आप इस तरह इसका इस्तेमाल कर सकते हैं:

    >>> instance = Something.objects.create(
           name='something', 
           data={'a': '1', 'b': '2'} 
          ) 
    >>> instance.data['a'] 
    '1'   
    >>> empty = Something.objects.create(name='empty') 
    >>> empty.data 
    {} 
    >>> empty.data['a'] = '1' 
    >>> empty.save() 
    >>> Something.objects.get(name='something').data['a'] 
    '1' 
    

    आप hstore क्षेत्रों के खिलाफ अनुक्रमित क्वेरी भेज सकते हैं:

    # equivalence 
    Something.objects.filter(data={'a': '1', 'b': '2'}) 
    
    # subset by key/value mapping 
    Something.objects.filter(data__a='1') 
    
    # subset by list of keys 
    Something.objects.filter(data__has_keys=['a', 'b']) 
    
    # subset by single key 
    Something.objects.filter(data__has_key='a')  
    

    JSONField:

    JSON/जेएसओएनबी फ़ील्ड किसी भी जेएसओएन-एन्कोडेबल डेटा प्रकार का समर्थन करता है, न केवल कुंजी/मूल्य ई जोड़े, लेकिन तेजी से और (JSONB के लिए) Hstore से अधिक कॉम्पैक्ट होने लगते हैं। कई संकुल सहित django-pgfields JSON/JSONB क्षेत्रों को लागू है, लेकिन Django 1.9, के रूप में JSONField है एक अंतर्निहित भंडारण के लिए JSONB का उपयोग कर। JSONField एचस्टोरफ़ील्ड के समान है, और बड़े शब्दकोशों के साथ बेहतर प्रदर्शन कर सकता है। यह तारों के अलावा अन्य प्रकारों का भी समर्थन करता है, जैसे पूर्णांक, बूलियन और नेस्टेड शब्दकोश।खोल में

    #app/models.py 
    from django.contrib.postgres.fields import JSONField 
    class Something(models.Model): 
        name = models.CharField(max_length=32) 
        data = JSONField(db_index=True) 
    

    बनाना:

    >>> instance = Something.objects.create(
           name='something', 
           data={'a': 1, 'b': 2, 'nested': {'c':3}} 
          ) 
    

    इंडेक्स्ड प्रश्नों HStoreField लिए लगभग समान हैं, घोंसले को छोड़कर संभव है। जटिल अनुक्रमणिका के लिए मैन्युअल रूप से निर्माण (या एक स्क्रिप्ट किए गए माइग्रेशन) की आवश्यकता हो सकती है।

    >>> Something.objects.filter(data__a=1) 
    >>> Something.objects.filter(data__nested__c=3) 
    >>> Something.objects.filter(data__has_key='a') 
    
  3. Django MongoDB

    या अन्य NoSQL Django रूपांतरों - उन लोगों के साथ आप पूरी तरह से गतिशील मॉडल हो सकता है।

    NoSQL Django पुस्तकालयों महान हैं, लेकिन ध्यान रखें कि वे 100% Django-संगत नहीं हैं, उदाहरण के लिए, मानक Django से Django-nonrel में माइग्रेट करने की अन्य बातों के अलावा ListField साथ ManyToMany को बदलने के लिए की आवश्यकता होगी।

    चेकआउट इस Django MongoDB उदाहरण:

    from djangotoolbox.fields import DictField 
    
    class Image(models.Model): 
        exif = DictField() 
    ... 
    
    >>> image = Image.objects.create(exif=get_exif_data(...)) 
    >>> image.exif 
    {u'camera_model' : 'Spamcams 4242', 'exposure_time' : 0.3, ...} 
    

    तुम भी किसी भी Django मॉडल की embedded lists बना सकते हैं:

    class Container(models.Model): 
        stuff = ListField(EmbeddedModelField()) 
    
    class FooModel(models.Model): 
        foo = models.IntegerField() 
    
    class BarModel(models.Model): 
        bar = models.CharField() 
    ... 
    
    >>> Container.objects.create(
        stuff=[FooModel(foo=42), BarModel(bar='spam')] 
    ) 
    
  4. Django-mutant: Dynamic models based on syncdb and South-hooks

    Django-mutant लागू पूरी तरह से गतिशील विदेशी कुंजी और M2M खेत। और Will Hardy और माइकल हॉल द्वारा अविश्वसनीय लेकिन कुछ हद तक हैक समाधान से प्रेरित है।

    इन सभी Django दक्षिण हुक, जो, Will Hardy's talk at DjangoCon 2011(इसे देख!) के अनुसार फिर भी मजबूत और उत्पादन (relevant source code) में परीक्षण कर रहे हैं पर आधारित हैं।

    पहले से implement thisMichael Hall था।

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

    आप माइकल हॉल lib का उपयोग कर रहे हैं, तो अपने कोड इस तरह दिखेगा:

    from dynamo import models 
    
    test_app, created = models.DynamicApp.objects.get_or_create(
             name='dynamo' 
            ) 
    test, created = models.DynamicModel.objects.get_or_create(
            name='Test', 
            verbose_name='Test Model', 
            app=test_app 
           ) 
    foo, created = models.DynamicModelField.objects.get_or_create(
            name = 'foo', 
            verbose_name = 'Foo Field', 
            model = test, 
            field_type = 'dynamiccharfield', 
            null = True, 
            blank = True, 
            unique = False, 
            help_text = 'Test field for Foo', 
           ) 
    bar, created = models.DynamicModelField.objects.get_or_create(
            name = 'bar', 
            verbose_name = 'Bar Field', 
            model = test, 
            field_type = 'dynamicintegerfield', 
            null = True, 
            blank = True, 
            unique = False, 
            help_text = 'Test field for Bar', 
           ) 
    
+31

यह सुंदर लानत पूरी तरह से है। – Edwin

+0

@GDorn, अपडेट के लिए धन्यवाद! –

+2

इस विषय को हाल ही में DjangoCon 2013 यूरोप में बात की गई थी: http://www.slideshare.net/schacki/django-dynamic-models20130502?from_search=2 और http://www.youtube.com/watch?v=67wcGdk4aCc –

3

इसके अलावा अनुसंधान पता चलता है कि इस Entity Attribute Value डिज़ाइन पैटर्न है, जो Django के लिए लागू किया गया है की कुछ हद तक एक विशेष मामला है कुछ पैकेजों द्वारा।

सबसे पहले, मूल eav-django प्रोजेक्ट है, जो पीईपीआई पर है।

दूसरा, पहली परियोजना का एक और हालिया कांटा है, django-eav जो मुख्य रूप से एक एएवी के उपयोग को डीजेंगो के अपने मॉडल या तीसरे पक्ष के ऐप्स में मॉडल के उपयोग की अनुमति देने के लिए एक रिफैक्टर है।

+0

मैं इसे विकी में शामिल करूंगा। –

+1

मैं दूसरी तरह के आसपास लोगों का तर्क था, कि EAV गतिशील मॉडलिंग की एक विशेष मामला है। इसका उपयोग "अर्थात् वेब" समुदाय में किया जाता है जहां इसे "ट्रिपल" या "क्वाड" कहा जाता है यदि इसमें एक अद्वितीय आईडी शामिल है। हालांकि, यह कभी भी एक तंत्र के रूप में कुशल होने की संभावना नहीं है जो SQL तालिकाओं को गतिशील रूप से बना और संशोधित कर सकता है। – Cerin

+0

@GDom आपकी पहली पसंद है eav-django? मेरा मतलब है कि आपने ऊपर कौन सा विकल्प चुना है? – Moreno

13

मैं Django-डाइनेमो विचार आगे धकेलने पर काम कर रहा है। परियोजना अभी भी अनियंत्रित है लेकिन आप https://github.com/charettes/django-mutant पर कोड पढ़ सकते हैं।

असल में एफके और एम 2 एम फ़ील्ड (contrib.related देखें) भी काम करते हैं और अपने स्वयं के कस्टम फ़ील्ड के लिए रैपर को परिभाषित करना भी संभव है।

मॉडल विकल्पों के लिए भी समर्थन है जैसे अद्वितीय_टोगेदर और ऑर्डरिंग प्लस मॉडल बेस, ताकि आप मॉडल प्रॉक्सी, सार या मिश्रित उप-वर्ग कर सकें।

मैं वास्तव में इन-मेमोरी लॉक तंत्र पर काम कर रहा हूं ताकि यह सुनिश्चित किया जा सके कि मॉडल परिभाषाओं को अप्रचलित परिभाषा का उपयोग करते हुए कई डीजेंगो चलने वाले उदाहरणों में साझा किया जा सके।

परियोजना अभी भी बहुत अल्फा है लेकिन यह मेरी परियोजना में से एक के लिए आधारशिला तकनीक है इसलिए मुझे इसे उत्पादन के लिए तैयार करना होगा। बड़ी योजना django-nonrel का समर्थन कर रही है ताकि हम mongodb ड्राइवर का लाभ उठा सकें।

+1

हाय, साइमन! मैंने आपके प्रोजेक्ट के लिंक [मेरे विकी उत्तर] (http://stackoverflow.com/a/7934577/497056) में इसे गिटूब पर बनाने के ठीक बाद शामिल किया है। :))) स्टैक ओवरफ्लो पर आपको देखकर अच्छा लगा! –

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