这几天准备写个工作流系统,因为工作流中一些模板的字段都是不固定的,于是思考有没有动态的模型字段,刚好找到了某个大神写的东西,觉得有用(还没有实践),于是记录一下。
原文地址: https://stackoverflow.com/questions/7933596/django-dynamic-model-fields
翻译地址:http://stackoverflow.org.cn/front/ask/view?ask_id=17340
我正在开发一个多租户应用程序,其中一些用户可以通过管理员定义自己的数据字段,以收集表单中的其他数据并报告数据。 后一位使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'),)
这有以下用途
tom_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,如上所列,因为它不会很好地与查询。
原文有四个采纳答案,但我觉得只有一种更合适,下面只记录那一张答案。
JSONField:
JSON / JSONB字段支持任何JSON的可编码的数据类型,而不仅仅是键/值对,但也往往是更快(对于JSONB)比Hstore更紧凑。几个包实现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')
感觉这个方法简单,可以试一下