Django-eav (el paquete original ya no se mantiene pero tiene algunos tenedores prósperos )
Esta solución se basa en el modelo de datos de Valor de atributo de entidad , esencialmente, utiliza varias tablas para almacenar atributos dinámicos de objetos. Lo mejor de esta solución es que:
- utiliza varios modelos Django puros y simples para representar campos dinámicos, lo que lo hace fácil de entender y agnóstico a la base de datos;
le permite adjuntar / desconectar eficazmente el almacenamiento dinámico de atributos al modelo Django con comandos simples como:
eav.unregister(Encounter)
eav.register(Patient)
Muy bien se integra con Django admin ;
Al mismo tiempo ser realmente poderoso.
Desventajas:
- No muy eficiente Esto es más una crítica del patrón EAV en sí, que requiere fusionar manualmente los datos de un formato de columna a un conjunto de pares clave-valor en el modelo.
- Más difícil de mantener. Mantener la integridad de los datos requiere una restricción de clave única de varias columnas, que puede ser ineficiente en algunas bases de datos.
- Deberá seleccionar una de las horquillas , ya que el paquete oficial ya no se mantiene y no hay un líder claro.
El uso es bastante sencillo:
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)
Campos Hstore, JSON o JSONB en PostgreSQL
PostgreSQL admite varios tipos de datos más complejos. La mayoría son compatibles con paquetes de terceros, pero en los últimos años Django los ha adoptado en django.contrib.postgres.fields.
HStoreField :
Django-hstore era originalmente un paquete de terceros, pero Django 1.8 agregó HStoreField como una función integrada, junto con varios otros tipos de campos compatibles con PostgreSQL.
Este enfoque es bueno en el sentido de que le permite tener lo mejor de ambos mundos: campos dinámicos y bases de datos relacionales. Sin embargo, hstore no es ideal en cuanto al rendimiento , especialmente si va a terminar almacenando miles de elementos en un campo. También solo admite cadenas de valores.
#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)
En el shell de Django puedes usarlo así:
>>> 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'
Puede emitir consultas indexadas en los campos de 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 :
Los campos JSON / JSONB admiten cualquier tipo de datos codificables JSON, no solo pares clave / valor, sino que también tienden a ser más rápidos y (para JSONB) más compactos que Hstore. Varios paquetes implementan campos JSON / JSONB, incluidos django-pgfields , pero a partir de Django 1.9, JSONField está integrado con JSONB para el almacenamiento.
JSONField es similar a HStoreField y puede funcionar mejor con diccionarios grandes. También admite tipos distintos de cadenas, como enteros, booleanos y diccionarios anidados.
#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)
Creando en el shell:
>>> instance = Something.objects.create(
name='something',
data={'a': 1, 'b': 2, 'nested': {'c':3}}
)
Las consultas indexadas son casi idénticas a HStoreField, excepto que es posible anidar. Los índices complejos pueden requerir la creación manual (o una migración programada).
>>> Something.objects.filter(data__a=1)
>>> Something.objects.filter(data__nested__c=3)
>>> Something.objects.filter(data__has_key='a')
Django MongoDB
U otras adaptaciones NoSQL Django: con ellas puede tener modelos totalmente dinámicos.
Las bibliotecas NoSQL Django son geniales, pero tenga en cuenta que no son 100% compatibles con Django, por ejemplo, para migrar a Django-nonrel desde Django estándar, deberá reemplazar ManyToMany con ListField, entre otras cosas.
Vea este ejemplo de 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, ...}
Incluso puede crear listas incrustadas de cualquier modelo de 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-mutante: modelos dinámicos basados en syncdb y South-hooks
Django-mutant implementa campos foráneos y m2m completamente dinámicos. Y está inspirado en soluciones increíbles pero algo hack de Will Hardy y Michael Hall.
Todos estos se basan en los ganchos Django South, que, según la charla de Will Hardy en DjangoCon 2011 (¡cuidado!) , Sin embargo, son robustos y probados en producción ( código fuente relevante ).
El primero en implementar esto fue Michael Hall .
Sí, esto es mágico, con estos enfoques puede lograr aplicaciones, modelos y campos de Django completamente dinámicos con cualquier base de datos relacional. ¿Pero a qué precio? ¿La estabilidad de la aplicación se verá afectada por el uso intensivo? Estas son las preguntas a considerar. Debe asegurarse de mantener un bloqueo adecuado para permitir solicitudes de alteración simultánea de la base de datos.
Si está utilizando Michael Halls lib, su código se verá así:
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',
)