update queryset manager python django dynamic django-models django-custom-manager

python - queryset - django update



Campos del modelo dinámico de Django (3)

He estado trabajando para impulsar la idea de django-dínamo. El proyecto aún no está documentado, pero puede leer el código en https://github.com/charettes/django-mutant .

En realidad, los campos FK y M2M (consulte contrib.related) también funcionan, e incluso es posible definir el contenedor para sus propios campos personalizados.

También hay soporte para las opciones de modelo, como unique_together y ordering plus, así como bases de modelo para que pueda subclasificar el modelo proxy, abstract o mixins.

De hecho, estoy trabajando en un mecanismo de bloqueo que no está en la memoria para asegurarme de que las definiciones del modelo se puedan compartir en múltiples instancias en ejecución de django mientras se evita que usen definiciones obsoletas.

El proyecto sigue siendo muy alfa, pero es una tecnología fundamental para uno de mis proyectos, así que tendré que llevarlo a producción listo. El gran plan también es compatible con django-nonrel para que podamos aprovechar el controlador de mongodb.

Estoy trabajando en una aplicación de varios usuarios en la que algunos usuarios pueden definir sus propios campos de datos (a través del administrador) para recopilar datos adicionales en formularios e informar sobre ellos. El último bit hace que JSONField no sea una gran opción, así que en su lugar tengo la siguiente solución:

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

Observe cómo CustomDataField tiene una ForeignKey to Site: cada sitio tendrá un conjunto diferente de campos de datos personalizados, pero usará la misma base de datos. Entonces los diversos campos de datos concretos se pueden definir como:

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''),)

Esto lleva al siguiente uso:

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?

Pero esto se siente muy torpe, particularmente con la necesidad de crear manualmente los datos relacionados y asociarlos con el modelo concreto. ¿Hay un mejor enfoque?

Opciones que han sido descartadas preventivamente:

  • SQL personalizado para modificar tablas sobre la marcha. En parte porque esto no se escalará y en parte porque es demasiado pirateo.
  • Soluciones sin esquemas como NoSQL. No tengo nada en contra de ellos, pero todavía no son una buena opción. En última instancia, estos datos se tipean y existe la posibilidad de utilizar una aplicación de informes de terceros.
  • JSONField, como se mencionó anteriormente, ya que no va a funcionar bien con las consultas.

Investigaciones adicionales revelan que este es un caso algo especial de patrón de diseño de Valor de atributo de la entidad , que ha sido implementado para Django por un par de paquetes.

Primero, está el proyecto original eav-django , que está en PyPi.

En segundo lugar, hay una bifurcación más reciente del primer proyecto, Django-eav que es principalmente un refactor para permitir el uso de EAV con los propios modelos o modelos de django en aplicaciones de terceros.


A partir de hoy, hay cuatro enfoques disponibles, dos de ellos requieren un cierto back-end de almacenamiento:

  1. Django-eav (el paquete original ya no se mantiene pero tiene algunas bifurcaciones )

    Esta solución se basa en el modelo de datos de Entity Attribute Value , esencialmente, usa varias tablas para almacenar atributos dinámicos de objetos. Grandes partes de esta solución es que:

    • utiliza varios modelos de Django simples y puros para representar campos dinámicos, lo que hace que sea fácil de entender y agnóstico de la base de datos;
    • le permite conectar / desconectar eficazmente el almacenamiento dinámico de atributos al modelo Django con comandos simples como:

      eav.unregister(Encounter) eav.register(Patient)

    • Se integra muy bien con el administrador de Django ;

    • Al mismo tiempo, ser realmente poderoso.

    Desventajas:

    • No muy eficiente. Esto es más una crítica del propio patrón de EAV, 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 El mantenimiento de la integridad de los datos requiere una restricción de clave única de varias columnas, que puede ser ineficiente en algunas bases de datos.
    • Tendrá que 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)

  2. Campos Hstore, JSON o JSONB en PostgreSQL

    PostgreSQL es compatible con 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 un built-in, junto con varios otros tipos de campos soportados por PostgreSQL.

    Este enfoque es bueno en un sentido que le permite tener lo mejor de ambos mundos: campos dinámicos y base de datos relacional. Sin embargo, hstore no es ideal para el rendimiento , especialmente si va a terminar almacenando miles de elementos en un campo. También solo admite cadenas para 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 caparazón 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 contra 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 :

    Los campos JSON / JSONB admiten cualquier tipo de datos codificables en 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 que incluyen django-pgfields , pero a partir de Django 1.9, JSONField está integrado usando JSONB para 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 guionizada).

    >>> Something.objects.filter(data__a=1) >>> Something.objects.filter(data__nested__c=3) >>> Something.objects.filter(data__has_key=''a'')

  3. Django MongoDB

    U otras adaptaciones de NoSQL Django, con ellas puedes tener modelos completamente 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, necesitará reemplazar ManyToMany con ListField entre otras cosas.

    Verifique 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 integradas 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'')] )

  4. Django-mutante: modelos dinámicos basados ​​en syncdb y South-hooks

    Django-mutante implementa campos Foreign Key y m2m totalmente dinámicos. Y está inspirado en soluciones increíbles pero algo hackosas de Will Hardy y Michael Hall.

    Todos estos están basados ​​en los ganchos Django South, que, de acuerdo con la charla de Will Hardy en DjangoCon 2011 mirenlo !) Son robustos y probados en producción ( código fuente relevante ).

    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 back-end de base de datos relacional. ¿Pero a qué precio? ¿La estabilidad de la aplicación sufrirá un uso intensivo? Estas son las preguntas a considerar. Debe asegurarse de mantener un lock adecuado para permitir solicitudes simultáneas de modificación de la base de datos.

    Si está utilizando la lib de Michael Halls, 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'', )