Django-eav (die Originalverpackung ist nicht mehr erhalten, hat aber einige blühende Gabeln )
Diese Lösung basiert auf dem Datenmodell " Entitätsattributwert". Im Wesentlichen werden mehrere Tabellen zum Speichern dynamischer Attribute von Objekten verwendet. Das Tolle an dieser Lösung ist, dass es:
- verwendet mehrere reine und einfache Django-Modelle zur Darstellung dynamischer Felder, wodurch es einfach zu verstehen und datenbankunabhängig ist;
Ermöglicht das effektive Anhängen / Trennen des dynamischen Attributspeichers an das Django-Modell mit einfachen Befehlen wie:
eav.unregister(Encounter)
eav.register(Patient)
Integriert sich gut in den Django-Administrator .
Gleichzeitig wirklich mächtig.
Nachteile:
- Nicht sehr effizient. Dies ist eher eine Kritik am EAV-Muster selbst, bei dem die Daten manuell aus einem Spaltenformat mit einer Reihe von Schlüssel-Wert-Paaren im Modell zusammengeführt werden müssen.
- Schwieriger zu pflegen. Die Aufrechterhaltung der Datenintegrität erfordert eine mehrspaltige eindeutige Schlüsselbeschränkung, die in einigen Datenbanken möglicherweise ineffizient ist.
- Sie müssen eine der Gabeln auswählen , da das offizielle Paket nicht mehr gepflegt wird und es keinen klaren Anführer gibt.
Die Verwendung ist ziemlich einfach:
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- oder JSONB-Felder in PostgreSQL
PostgreSQL unterstützt mehrere komplexere Datentypen. Die meisten werden über Pakete von Drittanbietern unterstützt, aber in den letzten Jahren hat Django sie in django.contrib.postgres.fields übernommen.
HStoreField :
Django-hstore war ursprünglich ein Paket von Drittanbietern, aber Django 1.8 fügte HStoreField als integriertes Element hinzu , zusammen mit mehreren anderen von PostgreSQL unterstützten Feldtypen.
Dieser Ansatz ist insofern gut, als Sie das Beste aus beiden Welten haben: dynamische Felder und relationale Datenbanken. In Bezug auf die Leistung ist hstore jedoch nicht ideal , insbesondere wenn Sie am Ende Tausende von Artikeln in einem Feld speichern möchten . Es werden auch nur Zeichenfolgen für Werte unterstützt.
#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)
In Djangos Shell können Sie es folgendermaßen verwenden:
>>> 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'
Sie können indizierte Abfragen für hstore-Felder ausgeben:
# 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-Felder unterstützen jeden JSON-codierbaren Datentyp, nicht nur Schlüssel / Wert-Paare, sondern sind auch tendenziell schneller und (für JSONB) kompakter als Hstore. Einige Pakete implementieren JSON / JSONB-Felder, einschließlich django-pgfields. Ab Django 1.9 ist JSONField jedoch in JSONB als Speicher integriert.
JSONField ähnelt HStoreField und kann bei großen Wörterbüchern eine bessere Leistung erzielen. Es werden auch andere Typen als Zeichenfolgen unterstützt, z. B. Ganzzahlen, Boolesche Werte und verschachtelte Wörterbücher.
#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)
Erstellen in der Shell:
>>> instance = Something.objects.create(
name='something',
data={'a': 1, 'b': 2, 'nested': {'c':3}}
)
Indizierte Abfragen sind nahezu identisch mit HStoreField, außer dass eine Verschachtelung möglich ist. Komplexe Indizes müssen möglicherweise manuell erstellt (oder per Skript migriert) werden.
>>> Something.objects.filter(data__a=1)
>>> Something.objects.filter(data__nested__c=3)
>>> Something.objects.filter(data__has_key='a')
Django MongoDB
Oder andere NoSQL Django-Anpassungen - mit ihnen können Sie volldynamische Modelle haben.
NoSQL-Django-Bibliotheken sind großartig, aber denken Sie daran, dass sie nicht zu 100% Django-kompatibel sind. Um beispielsweise von Standard-Django auf Django-nonrel zu migrieren, müssen Sie unter anderem ManyToMany durch ListField ersetzen .
Testen Sie dieses Django MongoDB-Beispiel:
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, ...}
Sie können sogar eingebettete Listen aller Django-Modelle erstellen :
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-Mutante: Dynamische Modelle basierend auf Syncdb und South-Hooks
Django-Mutante implementiert volldynamische Fremdschlüssel- und m2m-Felder. Und ist inspiriert von unglaublichen, aber etwas hackigen Lösungen von Will Hardy und Michael Hall.
All dies basiert auf Django South Hooks, die laut Will Hardys Vortrag auf der DjangoCon 2011 ( siehe da!) Trotzdem robust und in der Produktion getestet sind ( relevanter Quellcode ).
Der erste, der dies umsetzte, war Michael Hall .
Ja, das ist magisch. Mit diesen Ansätzen können Sie mit jedem relationalen Datenbank-Backend volldynamische Django-Apps, -Modelle und -Felder erzielen . Aber zu welchen Kosten? Wird die Stabilität der Anwendung bei starker Beanspruchung leiden? Dies sind die zu berücksichtigenden Fragen. Sie müssen sicherstellen, dass eine ordnungsgemäße Sperre vorhanden ist , um gleichzeitige Datenbankänderungsanforderungen zu ermöglichen.
Wenn Sie Michael Halls lib verwenden, sieht Ihr Code folgendermaßen aus:
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',
)