Django-eav (元のパッケージは管理されなくなりましたが、いくつかの繁栄しているフォークがあります)
このソリューションは、 Entity Attribute Valueデータ モデルに基づいており、基本的に、複数のテーブルを使用してオブジェクトの動的属性を格納します。このソリューションの優れた点は、次のとおりです。
欠点:
- あまり効率的ではありません。これは、列形式からモデル内のキーと値のペアのセットにデータを手動でマージする必要がある EAV パターン自体に対する批判です。
- 維持するのが難しくなります。データの整合性を維持するには、複数列の一意のキー制約が必要ですが、一部のデータベースでは効率が悪い場合があります。
- 公式パッケージはメンテナンスされなくなり、明確なリーダーが存在しないため、 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)
PostgreSQL の Hstore、JSON、または JSONB フィールド
PostgreSQL は、さらに複雑なデータ型をいくつかサポートしています。ほとんどはサードパーティのパッケージでサポートされていますが、近年 Django はそれらを django.contrib.postgres.fields に採用しています。
HStoreField :
Django-hstoreはもともとサードパーティのパッケージでしたが、Django 1.8 では組み込みとしてHStoreFieldが追加され、PostgreSQL がサポートする他のいくつかのフィールド タイプも追加されました。
このアプローチは、動的フィールドとリレーショナル データベースの両方の長所を活用できるという意味で優れています。ただし、hstore は、特に 1 つのフィールドに何千ものアイテムを格納することになる場合は、パフォーマンスの点で理想的ではありません。また、値として文字列のみをサポートします。
#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')
JSONフィールド:
JSON/JSONB フィールドは、キーと値のペアだけでなく、JSON でエンコード可能な任意のデータ型をサポートしますが、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')
ジャンゴ MongoDB
または、他の NoSQL Django の適応 - それらを使用すると、完全に動的なモデルを作成できます。
NoSQL Django ライブラリは優れていますが、100% Django と互換性があるわけではないことに注意してください。たとえば、標準の Django からDjango-nonrelに移行するには、ManyToMany をListFieldに置き換える必要があります。
この 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')]
)
Django-mutant: syncdb と South-hooks に基づく動的モデル
Django-mutantは、完全に動的な外部キーと m2m フィールドを実装しています。そして、 Will Hardyと Michael Hallによる信じられないほどハックなソリューションに触発されています。
これらはすべて Django South フックに基づいていますが、DjangoCon 2011 での Will Hardy の講演 (見てください!)によると、それでも堅牢であり、本番環境でテストされています (関連するソース コード)。
これを最初に実装したのはMichael Hallでした。
はい、これは魔法です。これらのアプローチを使用すると、リレーショナル データベース バックエンドで完全に動的な 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',
)