python - queryset - Django動態模型字段




objects get all django (2)

截至今天,有四種可用的方法,其中兩種需要特定的存儲後端:

  1. Django-eav (原始包裹不再被Django-eav ,但有一些欣欣向榮的叉子

    該解決方案基於實體屬性值數據模型,實質上,它使用多個表來存儲對象的動態屬性。 這個解決方案的重要部分是它:

    • 使用幾個純粹和簡單的Django模型來表示動態字段,這使得它易於理解和與數據庫無關;
    • 允許您使用簡單的命令有效地將動態屬性存儲附加到/分離到Django模型,如下所示:

      eav.unregister(Encounter)
      eav.register(Patient)
      
    • 與Django管理員很好地集成 ;

    • 同時真的很強大。

    缺點:

    • 效率不高。 這更多的是對EAV模式本身的批評,這需要手動將數據從列格式合併到模型中的一組鍵值對中。
    • 更難維護。 保持數據完整性需要多列唯一鍵約束,這在某些數據庫中可能效率低下。
    • 您需要選擇其中一種叉子 ,因為官方包裝不再保留,並且沒有明確的領導者。

    用法非常簡單:

    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的shell中,你可以像這樣使用它:

    >>> 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可編碼的數據類型,不僅僅是鍵/值對,而且往往比Hstore更快(對於JSONB)更緊湊。 有幾個包實現了JSON / JSONB字段,包括django-pgfields ,但從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)
    

    在shell中創建:

    >>> 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模型的嵌入式列表

    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:基於syncdb和South-hooks的動態模型

    Django-mutant實現完全動態的Foreign Key和m2m字段。 受到威爾哈迪和邁克爾霍爾令人難以置信但有些駭人的解決方案的啟發。

    所有這些都基於Django South的鉤子,根據Will Hardy在DjangoCon 2011上的講話 (觀看它!)仍然強大並且在生產( 相關源代碼 )中進行了測試。

    邁克爾·霍爾首先實現這一目標

    是的,這很神奇,通過這些方法,您可以實現具有任何關係數據庫後端的完全動態的Django應用程序,模型和字段 。 但費用是多少? 應用程序的穩定性是否會受到重用? 這些是需要考慮的問題。 您需要確保保持適當的lock以允許同時發生數據庫更改請求。

    如果您使用Michael Halls 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',
                   )
    

我正在開發一個多租戶應用程序,其中一些用戶可以定義他們自己的數據字段(通過管理員)來收集表單中的其他數據並報告數據。 後者使得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如何將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?

但是這感覺非常笨重,特別是需要手動創建相關數據並將其與具體模型相關聯。 有更好的方法嗎?

已被先發製人拋棄的選項:

  • 自定義SQL可以即時修改表。 部分原因是這不會擴展,部分是因為它太過分了。
  • 像NoSQL這樣的無模式解決方案。 我對他們沒有任何反應,但他們仍然不太合適。 最終這些數據輸入的,並且存在使用第三方報告應用程序的可能性。
  • JSONField,如上所列,因為它不能很好地處理查詢。

我一直在努力進一步推動django-dynamo的想法。 該項目仍未公開,但您可以通過https://github.com/charettes/django-mutant閱讀代碼。

實際上,FK和M2M字段(請參閱contrib.related)也可以工作,甚至可以為自己的自定義字段定義封裝。

還支持模型選項,如unique_together和訂購以及模型庫,以便您可以對模型代理,抽像或mixin進行子類化。

實際上,我正在研究一種不在內存中的鎖定機制,以確保可以跨多個django運行實例共享模型定義,同時防止它們使用過時的定義。

該項目仍然是非常流行的,但它是我的一個項目的基石技術,所以我必須將其投入生產。 大計劃也支持django-nonrel,所以我們可以利用mongodb驅動程序。







django-custom-manager