Ich arbeite an einer mandantenfähigen Anwendung, in der einige Benutzer ihre eigenen Datenfelder (über den Administrator) definieren können, um zusätzliche Daten in Formularen zu sammeln und über die Daten zu berichten. Das letztere Bit macht JSONField nicht zu einer großartigen Option, daher habe ich stattdessen die folgende Lösung:
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
Beachten Sie, dass CustomDataField einen ForeignKey to Site hat - jede Site verfügt über einen anderen Satz benutzerdefinierter Datenfelder, verwendet jedoch dieselbe Datenbank. Dann können die verschiedenen konkreten Datenfelder definiert werden als:
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'),)
Dies führt zu folgender Verwendung:
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?
Dies ist jedoch sehr umständlich, insbesondere angesichts der Notwendigkeit, die zugehörigen Daten manuell zu erstellen und mit dem konkreten Modell zu verknüpfen. Gibt es einen besseren Ansatz?
Optionen, die vorbeugend verworfen wurden:
- Benutzerdefiniertes SQL zum Ändern von Tabellen im laufenden Betrieb. Teilweise, weil dies nicht skalierbar ist und teilweise, weil es zu viel Hack ist.
- Schemalose Lösungen wie NoSQL. Ich habe nichts gegen sie, aber sie passen immer noch nicht gut zusammen. Letztlich wird diese Daten sind eingegeben, und die Möglichkeit besteht , von einer Drittanbieter - Reporting - Anwendung.
- JSONField, wie oben aufgeführt, da es mit Abfragen nicht gut funktioniert.
Antworten:
Derzeit stehen vier Ansätze zur Verfügung, von denen zwei ein bestimmtes Speicher-Backend erfordern:
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:
Ermöglicht das effektive Anhängen / Trennen des dynamischen Attributspeichers an das Django-Modell mit einfachen Befehlen wie:
Integriert sich gut in den Django-Administrator .
Gleichzeitig wirklich mächtig.
Nachteile:
Die Verwendung ist ziemlich einfach:
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.
In Djangos Shell können Sie es folgendermaßen verwenden:
Sie können indizierte Abfragen für hstore-Felder ausgeben:
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.
Erstellen in der Shell:
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.
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:
Sie können sogar eingebettete Listen aller Django-Modelle erstellen :
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:
quelle
Ich habe daran gearbeitet, die Django-Dynamo-Idee weiter voranzutreiben. Das Projekt ist noch nicht dokumentiert, aber Sie können den Code unter https://github.com/charettes/django-mutant lesen .
Tatsächlich funktionieren auch FK- und M2M-Felder (siehe Contrib.Related) und es ist sogar möglich, Wrapper für Ihre eigenen benutzerdefinierten Felder zu definieren.
Es gibt auch Unterstützung für Modelloptionen wie unique_together und order plus Model Base, sodass Sie Modell-Proxy, Abstract oder Mixins in Unterklassen unterteilen können.
Ich arbeite derzeit an einem nicht speicherinternen Sperrmechanismus, um sicherzustellen, dass Modelldefinitionen für mehrere Django-Instanzen gemeinsam genutzt werden können, während verhindert wird, dass sie veraltete Definitionen verwenden.
Das Projekt ist immer noch sehr Alpha, aber es ist ein Eckpfeiler für eines meiner Projekte, sodass ich es produktionsbereit machen muss. Der große Plan sieht auch die Unterstützung von Django-Nonrel vor, damit wir den Mongodb-Treiber nutzen können.
quelle
Weitere Untersuchungen haben ergeben, dass dies ein etwas spezieller Fall des Entity Attribute Value- Entwurfsmusters ist, das von einigen Paketen für Django implementiert wurde.
Erstens gibt es das ursprüngliche eav-django- Projekt, das auf PyPi läuft.
Zweitens gibt es eine neuere Abzweigung des ersten Projekts, django-eav, die in erster Linie ein Refactor ist, um die Verwendung von EAV mit djangos eigenen Modellen oder Modellen in Apps von Drittanbietern zu ermöglichen.
quelle