Django गतिशील मॉडल फ़ील्ड


161

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

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 के पास फॉरेनकेई टू साइट कैसे है - प्रत्येक साइट पर कस्टम डेटा फ़ील्ड का एक अलग सेट होगा, लेकिन एक ही डेटाबेस का उपयोग करें। फिर विभिन्न कंक्रीट डेटा फ़ील्ड को निम्न के रूप में परिभाषित किया जा सकता है:

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?

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

पहले से खाली कर दिए गए विकल्प:

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

6
पूर्व-खाली, यह इनमें से कोई भी प्रश्न नहीं है: stackoverflow.com/questions/7801729/… stackoverflow.com/questions/2854656/…
GDorn

जवाबों:


278

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

  1. Django-eav (मूल पैकेज अब बंद नहीं है, लेकिन कुछ संपन्न कांटे हैं )

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

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

      eav.unregister(Encounter)
      eav.register(Patient)
    • अच्छी तरह से Django व्यवस्थापक के साथ एकीकृत करता है ;

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

    downsides:

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

    उपयोग बहुत सीधा है:

    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. PostgreSQL में Hstore, JSON या JSONB फ़ील्ड

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

    HStoreField :

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

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

    #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 फ़ील्ड किसी भी JSON-encodable डेटा प्रकार का समर्थन करते हैं, न केवल कुंजी / मान जोड़े, बल्कि Hstore की तुलना में अधिक कॉम्पैक्ट और (JSONB के लिए) होते हैं। कई पैकेज django-pgfields सहित JSON / JSONB फ़ील्ड को कार्यान्वित करते हैं , लेकिन Django 1.9 के रूप में, JSONField स्टोरेज के लिए JSONB का उपयोग करके एक अंतर्निहित अंतर्निहित है। JSONField HStoreField के समान है, और बड़े शब्दकोशों के साथ बेहतर प्रदर्शन कर सकता है। यह स्ट्रिंग्स के अलावा अन्य प्रकारों का भी समर्थन करता है, जैसे कि पूर्णांक, बूलियन और नेस्टेड शब्दकोश।

    #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 के साथ कईToMany को बदलने की आवश्यकता होगी ।

    इस 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 मॉडल की एम्बेडेड सूची बना सकते हैं:

    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- उत्परिवर्ती: Syncdb और दक्षिण-हुक के आधार पर गतिशील मॉडल

    Django- उत्परिवर्ती पूरी तरह से गतिशील विदेशी कुंजी और m2m फ़ील्ड लागू करता है। और विल हार्डी और माइकल हॉल द्वारा अविश्वसनीय लेकिन कुछ हद तक समाधानों से प्रेरित है ।

    ये सभी Django के दक्षिण हुक पर आधारित हैं, जो कि, DjangoCon 2011 में वॉकी हार्डी की बात के अनुसार (इसे देखें!) फिर भी उत्पादन ( प्रासंगिक स्रोत कोड ) में मजबूत और परीक्षण किए गए हैं ।

    सबसे पहले इसे लागू करने के लिए माइकल हॉल था ।

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

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

    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',
                   )

3
: इस विषय हाल ही में DjangoCon 2013 यूरोप में के बारे में बात की गई थी slideshare.net/schacki/... और youtube.com/watch?v=67wcGdk4aCc
Aleck Landgraf

यह भी ध्यान देने योग्य हो सकता है कि Postgres> = 9.2 पर django-pgjson का उपयोग करने से postgresql के json क्षेत्र के प्रत्यक्ष उपयोग की अनुमति मिलती है। Django> = 1.7 पर, प्रश्नों के लिए फ़िल्टर एपीआई अपेक्षाकृत अधिक समझदार है। Postgres> = 9.4 तेजी से प्रश्नों के लिए बेहतर अनुक्रमित के साथ jsonb फ़ील्ड को भी अनुमति देता है।
GDorn

1
आज ध्यान में रखते हुए Django HStoreField और JSONField को गोद लेने के लिए। इसमें कुछ फॉर्म विजेट शामिल हैं जो भयानक नहीं हैं, लेकिन यदि आपको व्यवस्थापक में डेटा को ट्विस्ट करने की आवश्यकता है तो काम करें।
GDorn

13

मैं आगे django-dynamo विचार आगे बढ़ाने पर काम कर रहा हूँ। यह परियोजना अभी भी अनिर्दिष्ट है लेकिन आप https://github.com/charettes/django-mutant पर कोड पढ़ सकते हैं ।

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

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

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

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


1
हाय, साइमन! मैंने अपने प्रोजेक्ट का लिंक अपने विकी उत्तर में शामिल किया है , उसके बाद ही आपने इसे गितुब पर बनाया है। :))) आप stackoverflow पर देखकर अच्छा लगा!
इवान खारलामोव

4

आगे के शोध से पता चलता है कि यह एंटिटी एट्रीब्यूट वैल्यू डिज़ाइन पैटर्न का एक विशेष मामला है , जिसे Django के लिए एक-दो पैकेजों द्वारा लागू किया गया है।

सबसे पहले, मूल ईव-डेजैंगो परियोजना है, जो कि PyPi पर है।

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


मैं इसे विकि में शामिल करूंगा।
इवान खारलामोव

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

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

1
@ मोरेनो सही विकल्प आपके विशिष्ट उपयोग के मामले पर बहुत अधिक निर्भर करने वाला है। मैंने EAV और JsonFields दोनों को अलग-अलग कारणों से उपयोग किया है। बाद में सीधे Django द्वारा समर्थित है, इसलिए एक नई परियोजना के लिए मैं पहले उस का उपयोग करूंगा जब तक कि मुझे ईएवी टेबल पर क्वेरी करने में सक्षम होने की कोई विशिष्ट आवश्यकता नहीं होती। ध्यान दें कि आप JsonFields पर भी क्वेरी कर सकते हैं।
GDorn
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.