-
Django-eav https://github.com/mvpdev/django-eav(原始包不再维护,但有一些蓬勃发展的分叉 https://github.com/mvpdev/django-eav/network)
该解决方案基于实体属性值 https://en.wikipedia.org/wiki/Entity-attribute-value_model数据模型,本质上是使用多个表来存储对象的动态属性。该解决方案的重要部分在于:
- 使用几个纯粹且简单的 Django 模型来表示动态字段,这使得它易于理解并且与数据库无关;
-
允许您使用以下简单命令有效地将动态属性存储附加/分离到 Django 模型:
eav.unregister(Encounter)
eav.register(Patient)
与 Django 管理完美集成 https://github.com/mvpdev/django-eav/blob/master/eav/admin.py;
同时自己的实力也确实非常强大。
缺点:
- 效率不太高。这更多的是对 EAV 模式本身的批评,它需要手动将数据从列格式合并到模型中的一组键值对。
- 更难维护。维护数据完整性需要多列唯一键约束,这在某些数据库上可能效率低下。
- 您需要选择其中一个叉子 https://github.com/mvpdev/django-eav/network,因为官方包不再维护并且没有明确的领导者。
用法非常简单:
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)
-
PostgreSQL 中的 Hstore、JSON 或 JSONB 字段
PostgreSQL 支持多种更复杂的数据类型。大多数都是通过第三方包支持的,但近年来 Django 已将它们采用到 django.contrib.postgres.fields 中。
存储字段:
Django-hstore https://github.com/jordanm/django-hstore原本是第三方包,但是Django 1.8添加了存储字段 https://docs.djangoproject.com/en/1.8/ref/contrib/postgres/fields/#hstorefield作为内置字段类型,以及其他几种 PostgreSQL 支持的字段类型。
从某种意义上说,这种方法很好,它可以让您两全其美:动态字段和关系数据库。然而,hstore 是性能方面不理想 http://archives.postgresql.org/pgsql-performance/2011-05/msg00263.php,特别是如果您最终要在一个字段中存储数千个项目。它还仅支持值的字符串。
#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')
JSON字段:
JSON/JSONB 字段支持任何 JSON 可编码的数据类型,不仅仅是键/值对,而且往往比 Hstore 更快且(对于 JSONB)更紧凑。
多个包实现了 JSON/JSONB 字段,包括Django-pgfields https://django-pgfields.readthedocs.org/en/latest/fields.html,但从 Django 1.9 开始,JSON字段 https://docs.djangoproject.com/en/1.9/ref/contrib/postgres/fields/#jsonfield是一个内置的使用JSONB进行存储的。JSON字段与 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')
-
Django MongoDB http://django-mongodb-engine.readthedocs.org/en/latest/
或者其他 NoSQL Django 改编版本——使用它们您可以拥有完全动态的模型。
NoSQL Django 库很棒,但请记住,它们并不是 100% 与 Django 兼容,例如,迁移到Django-nonrel http://www.allbuttonspressed.com/projects/django-nonrel在标准 Django 中,您需要将 ManyToMany 替换为列表字段 https://stackoverflow.com/questions/3877246/django-nonrel-on-google-app-engine-implications-of-using-listfield-for-manytom除其他事项外。
查看这个 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, ...}
您甚至可以创建嵌入列表 http://django-mongodb.org/topics/embedded-models.html任何 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')]
)
-
Django-mutant:基于syncdb和South-hooks的动态模型 https://github.com/charettes/django-mutant
Django 突变体 https://github.com/charettes/django-mutant实现完全动态的外键和 m2m 字段。并受到令人难以置信但有些黑客解决方案的启发威尔·哈迪 http://dynamic-models.readthedocs.org/en/latest/index.html和迈克尔·霍尔。
所有这些都是基于 Django South hooks,根据威尔·哈迪 (Will Hardy) 在 DjangoCon 2011 上的演讲 http://blip.tv/djangocon-europe-2011/wednesday-1415-will-hardy-5311186 (看它!)尽管如此,仍然很强大并在生产中经过测试(相关源码 http://dynamic-models.readthedocs.org/en/latest/).
首先实施这个 https://bitbucket.org/mhall119/dynamo/overview was 迈克尔·霍尔 http://mhall119.com/2011/02/fun-with-django-meta-classes-and-dynamic-models/.
是的,这就是魔法,通过这些方法你可以实现完全动态的 Django 应用程序、模型和字段与任何关系数据库后端。但代价是什么?大量使用会影响应用程序的稳定性吗?这些都是需要考虑的问题。您需要确保保持适当的lock https://stackoverflow.com/questions/1123200/how-to-lock-a-critical-section-in-django以允许同时进行数据库更改请求。
如果您使用 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',
)