Django-eav (o pacote original não é mais mantido, mas possui alguns garfos prósperos )
Esta solução é baseada no modelo de dados Entity Attribute Value , essencialmente, usa várias tabelas para armazenar atributos dinâmicos de objetos. Grandes partes dessa solução é que ela:
- usa vários modelos Django puros e simples para representar campos dinâmicos, o que facilita o entendimento e a agnóstico de banco de dados;
permite conectar / desanexar efetivamente o armazenamento de atributos dinâmicos ao modelo do Django com comandos simples como:
eav.unregister(Encounter)
eav.register(Patient)
Integra-se perfeitamente com o administrador do Django ;
Ao mesmo tempo, é realmente poderoso.
Desvantagens:
- Não é muito eficiente. Isso é mais uma crítica ao próprio padrão EAV, que requer mesclar manualmente os dados de um formato de coluna para um conjunto de pares de valores-chave no modelo.
- Mais difícil de manter. A manutenção da integridade dos dados requer uma restrição de chave exclusiva de várias colunas, que pode ser ineficiente em alguns bancos de dados.
- Você precisará selecionar um dos garfos , já que o pacote oficial não é mais mantido e não há um líder claro.
O uso é bem direto:
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 ou JSONB no PostgreSQL
O PostgreSQL suporta vários tipos de dados mais complexos. A maioria é suportada por pacotes de terceiros, mas nos últimos anos o Django os adotou no django.contrib.postgres.fields.
HStoreField :
O Django-hstore era originalmente um pacote de terceiros, mas o Django 1.8 adicionou o HStoreField como um built-in, junto com vários outros tipos de campos suportados pelo PostgreSQL.
Essa abordagem é boa no sentido de permitir que você tenha o melhor dos dois mundos: campos dinâmicos e banco de dados relacional. No entanto, o hstore não é ideal em termos de desempenho , especialmente se você acabar armazenando milhares de itens em um campo. Ele também suporta apenas cadeias 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)
No shell do Django você pode usá-lo assim:
>>> 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'
Você pode emitir consultas indexadas nos campos 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 :
Os campos JSON / JSONB suportam qualquer tipo de dados codificado em JSON, não apenas pares de chave / valor, mas também tendem a ser mais rápidos e (para JSONB) mais compactos que o Hstore. Vários pacotes implementam campos JSON / JSONB, incluindo django-pgfields , mas a partir do Django 1.9, o JSONField é um built-in usando JSONB para armazenamento.
O JSONField é semelhante ao HStoreField e pode ter um desempenho melhor em dicionários grandes. Ele também suporta outros tipos de strings, como números inteiros, booleanos e dicionários aninhados.
#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)
Criando no shell:
>>> instance = Something.objects.create(
name='something',
data={'a': 1, 'b': 2, 'nested': {'c':3}}
)
As consultas indexadas são quase idênticas ao HStoreField, exceto que o aninhamento é possível. Índices complexos podem exigir criação manual (ou uma migração com script).
>>> Something.objects.filter(data__a=1)
>>> Something.objects.filter(data__nested__c=3)
>>> Something.objects.filter(data__has_key='a')
Django MongoDB
Ou outras adaptações NoSQL Django - com elas você pode ter modelos totalmente dinâmicos.
As bibliotecas NoSQL Django são ótimas, mas lembre-se de que elas não são 100% compatíveis com o Django, por exemplo, para migrar para o Django-nonrel a partir do Django padrão, você precisará substituir ManyToMany por ListField, entre outras coisas.
Veja este exemplo do 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, ...}
Você pode até criar listas incorporadas de qualquer modelo do 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: Modelos dinâmicos baseados em syncdb e South-hooks
O Django-mutant implementa campos de Chave Estrangeira e m2m totalmente dinâmicos. E é inspirado em soluções incríveis, mas um tanto hackistas, de Will Hardy e Michael Hall.
Tudo isso é baseado nos ganchos do Django Sul, que, de acordo com a palestra de Will Hardy no DjangoCon 2011 (assista!) São, no entanto, robustos e testados em produção ( código fonte relevante ).
O primeiro a implementar isso foi Michael Hall .
Sim, isso é mágico, com essas abordagens você pode obter aplicativos, modelos e campos Django totalmente dinâmicos com qualquer back-end de banco de dados relacional. Mas a que custo? A estabilidade da aplicação sofrerá com o uso pesado? Estas são as questões a serem consideradas. Você precisa garantir um bloqueio adequado para permitir pedidos simultâneos de alteração do banco de dados.
Se você estiver usando a biblioteca de Michael Halls, seu código ficará assim:
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',
)