Pracuję nad aplikacją z wieloma dzierżawcami , w której niektórzy użytkownicy mogą definiować własne pola danych (za pośrednictwem administratora), aby zbierać dodatkowe dane w formularzach i raportować dane. Ten ostatni bit sprawia, że JSONField nie jest świetną opcją, więc zamiast tego mam następujące rozwiązanie:
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
Zwróć uwagę, że CustomDataField ma ForeignKey to Site - każda witryna będzie miała inny zestaw niestandardowych pól danych, ale będzie używać tej samej bazy danych. Następnie różne konkretne pola danych można zdefiniować jako:
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'),)
Prowadzi to do następującego zastosowania:
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?
Ale wydaje się to bardzo niezgrabne, szczególnie w przypadku konieczności ręcznego tworzenia powiązanych danych i kojarzenia ich z konkretnym modelem. Czy jest lepsze podejście?
Opcje, które zostały odrzucone z wyprzedzeniem:
- Niestandardowy SQL do modyfikowania tabel w locie. Częściowo dlatego, że to się nie skaluje, a częściowo dlatego, że jest to zbyt duży hack.
- Rozwiązania bez schematu, takie jak NoSQL. Nie mam nic przeciwko nim, ale nadal nie pasują. Ostatecznie dane te są wpisywane na maszynie i istnieje możliwość korzystania z aplikacji raportującej innej firmy.
- JSONField, jak wymieniono powyżej, ponieważ nie będzie dobrze działać z zapytaniami.
Odpowiedzi:
Na dzień dzisiejszy dostępne są cztery podejścia, z których dwa wymagają określonego zaplecza pamięci masowej:
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:
pozwala efektywnie dołączać / odłączać magazyn atrybutów dynamicznych do modelu Django za pomocą prostych poleceń, takich jak:
Ładnie integruje się z administratorem Django ;
Jednocześnie jest naprawdę potężny.
Wady:
Użycie jest dość proste:
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.
W powłoce Django możesz tego użyć w następujący sposób:
Możesz wysyłać zindeksowane zapytania do pól hstore:
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.
Tworzenie w powłoce:
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).
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:
Możesz nawet tworzyć osadzone listy dowolnych modeli Django:
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:
źródło
Pracowałem nad dalszym rozwinięciem idei django-dynamo. Projekt jest nadal nieudokumentowany, ale możesz przeczytać kod na https://github.com/charettes/django-mutant .
W rzeczywistości pola FK i M2M (patrz contrib.related) również działają, a nawet można zdefiniować opakowanie dla własnych pól niestandardowych.
Dostępna jest również obsługa opcji modelu, takich jak unique_together i ordering oraz podstawy modelu, dzięki czemu można podklasować model proxy, abstrakcyjny lub mieszany.
W rzeczywistości pracuję nad mechanizmem blokującym, który nie jest w pamięci, aby upewnić się, że definicje modeli mogą być współużytkowane przez wiele uruchomionych instancji django, jednocześnie zapobiegając używaniu przestarzałych definicji.
Projekt jest nadal bardzo alfa, ale jest podstawą technologii jednego z moich projektów, więc będę musiał zabrać go do produkcji. Wielki plan obejmuje również obsługę django-nonrel, abyśmy mogli wykorzystać sterownik mongodb.
źródło
Dalsze badania ujawniają, że jest to nieco szczególny przypadek wzorca projektowego wartości atrybutu jednostki , który został zaimplementowany w Django przez kilka pakietów.
Po pierwsze, jest oryginalny projekt eav-django , który jest na PyPi.
Po drugie, istnieje nowszy fork pierwszego projektu, django-eav, który jest przede wszystkim refaktorem umożliwiającym używanie EAV z własnymi modelami django lub modelami w aplikacjach innych firm.
źródło