Django-eav (il pacchetto originale non è più mantenuto ma ha alcune forcelle fiorenti )
Questa soluzione si basa sul modello di dati Valore attributo entità , essenzialmente utilizza diverse tabelle per memorizzare gli attributi dinamici degli oggetti. Gran parte di questa soluzione è che:
- utilizza diversi modelli Django puri e semplici per rappresentare i campi dinamici, il che rende semplice da capire e indipendente dal database;
consente di collegare / scollegare in modo efficace la memorizzazione dinamica degli attributi nel modello Django con semplici comandi come:
eav.unregister(Encounter)
eav.register(Patient)
Si integra perfettamente con l'amministratore di Django ;
Allo stesso tempo, è davvero potente.
Svantaggi:
- Non molto efficiente. Questa è più una critica al modello EAV stesso, che richiede l'unione manuale dei dati da un formato di colonna a un set di coppie chiave-valore nel modello.
- Più difficile da mantenere. Il mantenimento dell'integrità dei dati richiede un vincolo chiave univoco a più colonne, che può essere inefficace su alcuni database.
- Dovrai selezionare una delle forcelle , poiché il pacchetto ufficiale non è più mantenuto e non esiste un leader chiaro.
L'utilizzo è piuttosto semplice:
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)
Campi Hstore, JSON o JSONB in PostgreSQL
PostgreSQL supporta diversi tipi di dati più complessi. La maggior parte sono supportati da pacchetti di terze parti, ma negli ultimi anni Django li ha adottati in django.contrib.postgres.fields.
HStoreField :
Django-hstore era originariamente un pacchetto di terze parti, ma Django 1.8 ha aggiunto HStoreField come integrato, insieme a molti altri tipi di campi supportati da PostgreSQL.
Questo approccio è buono in un certo senso che ti consente di avere il meglio di entrambi i mondi: campi dinamici e database relazionale. Tuttavia, hstore non è l' ideale dal punto di vista delle prestazioni , soprattutto se si intende archiviare migliaia di articoli in un campo. Supporta anche solo stringhe per valori.
#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)
Nella shell di Django puoi usarlo in questo modo:
>>> 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'
È possibile inviare query indicizzate nei campi 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 :
I campi JSON / JSONB supportano qualsiasi tipo di dati codificabile JSON, non solo coppie chiave / valore, ma tendono anche a essere più veloci e (per JSONB) più compatti di Hstore. Diversi pacchetti implementano i campi JSON / JSONB tra cui django-pgfields , ma a partire da Django 1.9, JSONField è un JSONB incorporato che utilizza per l'archiviazione.
JSONField è simile a HStoreField e potrebbe funzionare meglio con dizionari di grandi dimensioni. Supporta anche tipi diversi dalle stringhe, come numeri interi, booleani e dizionari nidificati.
#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)
Creare nella shell:
>>> instance = Something.objects.create(
name='something',
data={'a': 1, 'b': 2, 'nested': {'c':3}}
)
Le query indicizzate sono quasi identiche a HStoreField, tranne che è possibile l'annidamento. Gli indici complessi possono richiedere la creazione manuale (o una migrazione tramite script).
>>> Something.objects.filter(data__a=1)
>>> Something.objects.filter(data__nested__c=3)
>>> Something.objects.filter(data__has_key='a')
Django MongoDB
O altri adattamenti NoSQL Django - con loro puoi avere modelli completamente dinamici.
Le librerie NoSQL Django sono fantastiche, ma tieni presente che non sono compatibili al 100% con Django, ad esempio, per migrare a Django-nonrel dallo standard Django dovrai sostituire ManyToMany con ListField tra le altre cose.
Dai un'occhiata a questo esempio di 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, ...}
Puoi persino creare elenchi incorporati di qualsiasi modello 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: modelli dinamici basati su syncdb e ganci sud
Il mutante Django implementa campi chiave dinamica e m2m completamente dinamici. Ed è ispirato da soluzioni incredibili ma in qualche modo hacker di Will Hardy e Michael Hall.
Tutti questi sono basati su ami Django South, che, secondo il discorso di Will Hardy a DjangoCon 2011 (guardalo!) Sono comunque robusti e testati in produzione ( codice sorgente pertinente ).
Il primo ad implementare questo fu Michael Hall .
Sì, questo è magico, con questi approcci è possibile ottenere app, modelli e campi Django completamente dinamici con qualsiasi backend di database relazionale. Ma a quale costo? La stabilità dell'applicazione ne risentirà in caso di uso intenso? Queste sono le domande da considerare. È necessario assicurarsi di mantenere un blocco adeguato per consentire richieste di modifica simultanea del database.
Se stai usando la libreria Michael Halls, il tuo codice sarà simile al seguente:
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',
)