Django-eav (оригинальная упаковка больше не ухожена, но имеет несколько процветающих вилок )
Это решение основано на модели данных Entity Attribute Value , по сути, оно использует несколько таблиц для хранения динамических атрибутов объектов. Отличительной чертой этого решения является то, что оно:
- использует несколько чистых и простых моделей 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)
Поля Hstore, JSON или JSONB в PostgreSQL
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, не только пары ключ / значение, но также имеют тенденцию быть быстрее и (для 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)
Создание в оболочке:
>>> 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')
Джанго МонгоДБ
Или другие адаптации NoSQL Django - с ними вы можете иметь полностью динамические модели.
Библиотеки NoSQL Django хороши, но имейте в виду, что они не на 100% совместимы с Django, например, для перехода на Django-nonrel из стандартного Django вам необходимо будет заменить 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')]
)
Джанго-мутант: динамические модели, основанные на syncdb и South-hooks
Джанго-мутант реализует полностью динамические поля Foreign Key и m2m. И вдохновлен невероятными, но несколько хакерскими решениями Уиллом Харди и Майклом Холлом.
Все они основаны на хуках Django South, которые, согласно докладу Уилла Харди на DjangoCon 2011 (смотрите!) , Тем не менее, надежны и протестированы в производстве ( соответствующий исходный код ).
Первым для реализации этого был Майкл Холл .
Да, это волшебство, с помощью этих подходов вы можете создавать полностью динамические приложения, модели и поля 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',
)