Django-eav (oryginalny pakiet nie jest już utrzymywany, ale ma kilka dobrze prosperujących rozwidleń )
To rozwiązanie jest oparte na modelu danych Entity Attribute Value , zasadniczo wykorzystuje kilka tabel do przechowywania dynamicznych atrybutów obiektów. Wspaniałe strony dotyczące tego rozwiązania są takie, że:
- używa kilku czystych i prostych modeli Django do reprezentowania pól dynamicznych, co ułatwia zrozumienie i jest niezależne od bazy danych;
pozwala efektywnie dołączać / odłączać magazyn atrybutów dynamicznych do modelu Django za pomocą prostych poleceń, takich jak:
eav.unregister(Encounter)
eav.register(Patient)
Ładnie integruje się z administratorem Django ;
Jednocześnie jest naprawdę potężny.
Wady:
- Niezbyt wydajne. Jest to raczej krytyka samego wzorca EAV, który wymaga ręcznego scalania danych z formatu kolumny do zestawu par klucz-wartość w modelu.
- Trudniejsze w utrzymaniu. Utrzymanie integralności danych wymaga ograniczenia klucza unikatowego w wielu kolumnach, które może być nieefektywne w niektórych bazach danych.
- Będziesz musiał wybrać jeden z wideł , ponieważ oficjalny pakiet nie jest już utrzymywany i nie ma wyraźnego lidera.
Użycie jest dość proste:
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, pola JSON lub JSONB w PostgreSQL
PostgreSQL obsługuje kilka bardziej złożonych typów danych. Większość jest obsługiwana przez pakiety firm trzecich, ale w ostatnich latach Django zaadoptowało je do django.contrib.postgres.fields.
HStoreField :
Django-hstore był pierwotnie pakietem innej firmy, ale Django 1.8 dodał HStoreField jako wbudowany, wraz z kilkoma innymi typami pól obsługiwanymi przez PostgreSQL.
To podejście jest dobre w tym sensie, że pozwala uzyskać to, co najlepsze z obu światów: pola dynamiczne i relacyjną bazę danych. Jednak hstore nie jest idealnym rozwiązaniem pod względem wydajności , zwłaszcza jeśli zamierzasz przechowywać tysiące elementów na jednym polu. Obsługuje również tylko ciągi dla wartości.
#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)
W powłoce Django możesz tego użyć w następujący sposób:
>>> 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'
Możesz wysyłać zindeksowane zapytania do pól 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 :
Pola JSON / JSONB obsługują dowolny typ danych zakodowany w formacie JSON, nie tylko pary klucz / wartość, ale są również szybsze i (w przypadku JSONB) bardziej zwarte niż Hstore. Kilka pakietów implementuje pola JSON / JSONB, w tym django-pgfields , ale od Django 1.9, JSONField jest wbudowanym narzędziem używającym JSONB do przechowywania.
JSONField jest podobny do HStoreField i może działać lepiej z dużymi słownikami. Obsługuje również typy inne niż ciągi, takie jak liczby całkowite, wartości logiczne i zagnieżdżone słowniki.
#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)
Tworzenie w powłoce:
>>> instance = Something.objects.create(
name='something',
data={'a': 1, 'b': 2, 'nested': {'c':3}}
)
Zapytania indeksowane są prawie identyczne z HStoreField, z wyjątkiem możliwości zagnieżdżania. Złożone indeksy mogą wymagać ręcznego utworzenia (lub migracji skryptowej).
>>> Something.objects.filter(data__a=1)
>>> Something.objects.filter(data__nested__c=3)
>>> Something.objects.filter(data__has_key='a')
Django MongoDB
Lub inne adaptacje NoSQL Django - dzięki nim możesz mieć w pełni dynamiczne modele.
Biblioteki NoSQL Django są świetne, ale pamiętaj, że nie są w 100% kompatybilne z Django, na przykład, aby migrować do Django-nonrel ze standardowego Django, będziesz musiał między innymi zastąpić ManyToMany ListField .
Sprawdź ten przykład 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, ...}
Możesz nawet tworzyć osadzone listy dowolnych modeli 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: Dynamiczne modele oparte na syncdb i South-hookach
Django-mutant implementuje w pełni dynamiczne pola klucza obcego i m2m. I jest inspirowany niesamowitymi, ale nieco hackerskimi rozwiązaniami autorstwa Willa Hardy'ego i Michaela Halla.
Wszystko to jest oparte na hookach Django South, które, zgodnie z przemówieniem Willa Hardy'ego na DjangoCon 2011 (patrzcie!), Są jednak solidne i przetestowane w produkcji ( odpowiedni kod źródłowy ).
Po pierwsze, aby zaimplementować to był Michael Hall .
Tak, to jest magia, dzięki takim podejściom można osiągnąć w pełni dynamiczne aplikacje, modele i pola Django z dowolnym zapleczem relacyjnej bazy danych. Ale jakim kosztem? Czy stabilność aplikacji ucierpi przy intensywnym użytkowaniu? Oto pytania, które należy rozważyć. Musisz upewnić się, że utrzymujesz odpowiednią blokadę , aby umożliwić jednoczesne żądania zmiany bazy danych.
Jeśli używasz biblioteki Michael Halls, Twój kod będzie wyglądał następująco:
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',
)