Я работаю над многопользовательским приложением, в котором некоторые пользователи могут определять свои собственные поля данных (через администратора), чтобы собирать дополнительные данные в формах и сообщать о них. Последний бит делает 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-eav (оригинальная упаковка больше не ухожена, но имеет несколько процветающих вилок )
Это решение основано на модели данных Entity Attribute Value , по сути, оно использует несколько таблиц для хранения динамических атрибутов объектов. Отличительной чертой этого решения является то, что оно:
позволяет эффективно подключать / отключать хранилище динамических атрибутов к модели Django с помощью простых команд, таких как:
Хорошо интегрируется с администратором Django ;
В то же время быть действительно мощным.
Недостатки:
Использование довольно просто:
Поля Hstore, JSON или JSONB в PostgreSQL
PostgreSQL поддерживает несколько более сложных типов данных. Большинство из них поддерживаются сторонними пакетами, но в последние годы Django перенес их в django.contrib.postgres.fields.
HStoreField :
Django-hstore изначально был сторонним пакетом, но Django 1.8 добавил HStoreField в качестве встроенного, наряду с несколькими другими типами полей, поддерживаемыми PostgreSQL.
Этот подход хорош в том смысле, что он позволяет вам иметь лучшее из обоих миров: динамические поля и реляционная база данных. Однако hstore не идеален с точки зрения производительности , особенно если вы собираетесь хранить тысячи предметов в одном поле. Он также поддерживает только строки для значений.
В оболочке Django вы можете использовать это так:
Вы можете выполнить индексированные запросы к полям hstore:
JSONField :
Поля JSON / JSONB поддерживают любой тип данных, кодируемый JSON, не только пары ключ / значение, но также имеют тенденцию быть быстрее и (для JSONB) более компактными, чем Hstore. Несколько пакетов реализуют поля JSON / JSONB, в том числе django-pgfields , но начиная с Django 1.9, JSONField является встроенным, использующим JSONB для хранения. JSONField похож на HStoreField и может работать лучше с большими словарями. Он также поддерживает типы, отличные от строк, такие как целые числа, логические значения и вложенные словари.
Создание в оболочке:
Индексированные запросы практически идентичны HStoreField, за исключением возможного вложения. Сложные индексы могут потребовать создания вручную (или миграции по сценарию).
Джанго МонгоДБ
Или другие адаптации NoSQL Django - с ними вы можете иметь полностью динамические модели.
Библиотеки NoSQL Django хороши, но имейте в виду, что они не на 100% совместимы с Django, например, для перехода на Django-nonrel из стандартного Django вам необходимо будет заменить ManyToMany на ListField, среди прочего.
Проверьте этот пример Django MongoDB:
Вы даже можете создавать встроенные списки любых моделей Django:
Джанго-мутант: динамические модели, основанные на syncdb и South-hooks
Джанго-мутант реализует полностью динамические поля Foreign Key и m2m. И вдохновлен невероятными, но несколько хакерскими решениями Уиллом Харди и Майклом Холлом.
Все они основаны на хуках Django South, которые, согласно докладу Уилла Харди на DjangoCon 2011 (смотрите!) , Тем не менее, надежны и протестированы в производстве ( соответствующий исходный код ).
Первым для реализации этого был Майкл Холл .
Да, это волшебство, с помощью этих подходов вы можете создавать полностью динамические приложения, модели и поля Django с любым бэкэндом реляционной базы данных. Но какой ценой? Будет ли стабильность приложения ухудшаться при интенсивном использовании? Это вопросы для рассмотрения. Вы должны быть уверены, что сохраняете правильную блокировку , чтобы позволить одновременные запросы на изменение базы данных.
Если вы используете Michael Halls lib, ваш код будет выглядеть так:
источник
Я работал над продвижением идеи Джанго-Динамо. Проект по-прежнему недокументирован, но вы можете прочитать код по адресу https://github.com/charettes/django-mutant .
На самом деле поля FK и M2M (см. Contrib.related) также работают, и даже можно определить оболочку для ваших собственных настраиваемых полей.
Также есть поддержка опций модели, таких как unique_together и ordering plus Основы модели, так что вы можете создавать подклассы моделей прокси, абстракций или миксинов.
На самом деле я работаю над механизмом блокировки не в памяти, чтобы убедиться, что определения моделей могут быть общими для нескольких запущенных экземпляров django, не позволяя им использовать устаревшее определение.
Проект все еще очень альфа, но это краеугольная технология для одного из моих проектов, поэтому мне придется довести его до готовности. Большой план также поддерживает django-nonrel, чтобы мы могли использовать драйвер mongodb.
источник
Дальнейшие исследования показывают, что это несколько особый случай шаблона проектирования Entity Attribute Value , который был реализован для Django несколькими пакетами.
Во-первых, это оригинальный проект eav-django , который находится на PyPi.
Во-вторых, есть более поздняя версия первого проекта, django-eav, которая в первую очередь является рефакторингом, позволяющим использовать EAV с собственными моделями или моделями django в сторонних приложениях.
источник