python django dictionary django-models

python - Convierta el objeto Django Model a dict con todos los campos intactos



dictionary django-models (9)

¿Cómo se puede convertir un objeto de modelo django en un dict con todos sus campos? Todo idealmente incluye claves externas y campos con editable = False.

Déjame elaborar. Digamos que tengo un modelo django como el siguiente:

from django.db import models class OtherModel(models.Model): pass class SomeModel(models.Model): value = models.IntegerField() value2 = models.IntegerField(editable=False) created = models.DateTimeField(auto_now_add=True) reference1 = models.ForeignKey(OtherModel, related_name="ref1") reference2 = models.ManyToManyField(OtherModel, related_name="ref2")

En la terminal, he hecho lo siguiente:

other_model = OtherModel() other_model.save() instance = SomeModel() instance.value = 1 instance.value2 = 2 instance.reference1 = other_model instance.save() instance.reference2.add(other_model) instance.save()

Quiero convertir esto al siguiente diccionario:

{''created'': datetime.datetime(2015, 3, 16, 21, 34, 14, 926738, tzinfo=<UTC>), u''id'': 1, ''reference1'': 1, ''reference2'': [1], ''value'': 1, ''value2'': 2}

Preguntas con respuestas insatisfactorias:

Django: Convertir un conjunto completo de objetos de un Modelo en un solo diccionario

¿Cómo puedo convertir los objetos del Modelo Django en un diccionario y todavía tengo sus claves foráneas?


¡La solución de @Zag fue maravillosa!

Añadiría, sin embargo, una condición para datefields para que sea amigable con JSON.

Ronda de bonificación

Si desea un modelo de django que tenga una mejor pantalla de línea de comandos de python, haga que sus modelos hagan una clase infantil de la siguiente manera:

from django.db import models from django.db.models.fields.related import ManyToManyField class PrintableModel(models.Model): def __repr__(self): return str(self.to_dict()) def to_dict(self): opts = self._meta data = {} for f in opts.concrete_fields + opts.many_to_many: if isinstance(f, ManyToManyField): if self.pk is None: data[f.name] = [] else: data[f.name] = list(f.value_from_object(self).values_list(''pk'', flat=True)) elif isinstance(f, DateTimeField): if f.value_from_object(self) is not None: data[f.name] = f.value_from_object(self).timestamp() else: data[f.name] = None else: data[f.name] = f.value_from_object(self) return data class Meta: abstract = True

Entonces, por ejemplo, si definimos nuestros modelos como tales:

class OtherModel(PrintableModel): pass class SomeModel(PrintableModel): value = models.IntegerField() value2 = models.IntegerField(editable=False) created = models.DateTimeField(auto_now_add=True) reference1 = models.ForeignKey(OtherModel, related_name="ref1") reference2 = models.ManyToManyField(OtherModel, related_name="ref2")

Llamar a SomeModel.objects.first() ahora da salida como esta:

{''created'': 1426552454.926738, ''value'': 1, ''value2'': 2, ''reference1'': 1, u''id'': 1, ''reference2'': [1]}


(no fue para hacer el comentario)

Ok, en realidad no depende de los tipos de esa manera. Puede que haya entendido mal la pregunta original aquí, así que discúlpeme si ese es el caso. Si creas serliazers.py entonces allí creas clases que tienen metaclases.

Class MyModelSerializer(serializers.ModelSerializer): class Meta: model = modelName fields =(''csv'',''of'',''fields'')

Luego, cuando obtenga los datos en la clase de vista, puede:

model_data - Model.objects.filter(...) serializer = MyModelSerializer(model_data, many=True) return Response({''data'': serilaizer.data}, status=status.HTTP_200_OK)

Eso es más o menos lo que tengo en una variedad de lugares y devuelve un buen JSON a través del JSONRenderer.

Como dije, esto es cortesía de DjangoRestFramework, así que vale la pena investigar eso.


Encontré una solución clara para llegar al resultado:

Supongamos que tiene un objeto modelo o :

Solo llama:

type(o).objects.filter(pk=o.pk).values().first()


Hay muchas maneras de convertir la instancia a un diccionario, con diversos grados de manejo de casos en la esquina y cercanía al resultado deseado.

1. instance.__dict__

instance.__dict__

que devuelve

{''_reference1_cache'': <OtherModel: OtherModel object>, ''_state'': <django.db.models.base.ModelState at 0x1f63310>, ''created'': datetime.datetime(2014, 2, 21, 4, 38, 51, 844795, tzinfo=<UTC>), ''id'': 1L, ''reference1_id'': 1L, ''value'': 1, ''value2'': 2}

Este es de lejos el más simple, pero falta la referencia2, la referencia1 tiene un nombre erróneo y tiene dos elementos adicionales.

2. model_to_dict

from django.forms.models import model_to_dict model_to_dict(instance)

que devuelve

{u''id'': 1L, ''reference1'': 1L, ''reference2'': [1L], ''value'': 1}

Este es el único con referencia2, pero le faltan los campos no editables.

3. model_to_dict con campos

from django.forms.models import model_to_dict model_to_dict(instance, fields=[field.name for field in instance._meta.fields])

que devuelve

{u''id'': 1L, ''reference1'': 1L, ''value'': 1}

Esto es estrictamente peor que la invocación estándar de model_to_dict.

4. query_set.values ​​()

SomeModel.objects.filter(id=instance.id).values()[0]

que devuelve

{''created'': datetime.datetime(2014, 2, 21, 4, 38, 51, tzinfo=<UTC>), u''id'': 1L, ''reference1_id'': 1L, ''value'': 1L, ''value2'': 2L}

Este es el mismo resultado que la instance.__dict__ pero sin los campos adicionales.

5. Función personalizada

El código para model_to_dict de django tuvo la mayor parte de la respuesta. Se eliminaron explícitamente los campos no editables, por lo que al eliminar esa comprobación se obtiene el siguiente código que se comporta como se desea:

from django.db.models.fields.related import ManyToManyField def to_dict(instance): opts = instance._meta data = {} for f in opts.concrete_fields + opts.many_to_many: if isinstance(f, ManyToManyField): if instance.pk is None: data[f.name] = [] else: data[f.name] = list(f.value_from_object(instance).values_list(''pk'', flat=True)) else: data[f.name] = f.value_from_object(instance) return data

Si bien esta es la opción más complicada, llamar a to_dict(instance) nos da exactamente el resultado deseado:

{''created'': datetime.datetime(2015, 3, 16, 21, 34, 14, 926738, tzinfo=<UTC>), u''id'': 1, ''reference1'': 1, ''reference2'': [1], ''value'': 1, ''value2'': 2}

Ronda de bonificación

Si desea un modelo de django que tenga una mejor pantalla de línea de comandos de python, haga que sus modelos hagan una clase infantil de la siguiente manera:

from django.db import models from django.db.models.fields.related import ManyToManyField class PrintableModel(models.Model): def __repr__(self): return str(self.to_dict()) def to_dict(self): opts = self._meta data = {} for f in opts.concrete_fields + opts.many_to_many: if isinstance(f, ManyToManyField): if self.pk is None: data[f.name] = [] else: data[f.name] = list(f.value_from_object(self).values_list(''pk'', flat=True)) else: data[f.name] = f.value_from_object(self) return data class Meta: abstract = True

Entonces, por ejemplo, si definimos nuestros modelos como tales:

class OtherModel(PrintableModel): pass class SomeModel(PrintableModel): value = models.IntegerField() value2 = models.IntegerField(editable=False) created = models.DateTimeField(auto_now_add=True) reference1 = models.ForeignKey(OtherModel, related_name="ref1") reference2 = models.ManyToManyField(OtherModel, related_name="ref2")

Llamar a SomeModel.objects.first() ahora da salida como esta:

{''created'': datetime.datetime(2015, 3, 16, 21, 34, 14, 926738, tzinfo=<UTC>), ''value'': 1, ''value2'': 2, ''reference1'': 1, u''id'': 1, ''reference2'': [1]}


La manera más simple,

  1. Si su consulta es Model.Objects.get ():

    get () devolverá una sola instancia para que pueda dirigir el uso de __dict__ desde su instancia

    model_dict = Model.Objects.get().__dict__

  2. para filter () / all ():

    all () / filter () devolverá la lista de instancias para que pueda usar values() para obtener la lista de objetos.

    model_values ​​= Model.Objects.all (). values ​​()


Muchas soluciones interesantes aquí. Mi solución fue agregar un método as_dict a mi modelo con una comprensión dict.

def as_dict(self): return dict((f.name, getattr(self, f.name)) for f in self._meta.fields)

Como beneficio adicional, esta solución combinada con una lista de comprensión sobre una consulta es una buena solución si desea exportar sus modelos a otra biblioteca. Por ejemplo, volcando sus modelos en un marco de datos de pandas:

pandas_awesomeness = pd.DataFrame([m.as_dict() for m in SomeModel.objects.all()])


Quizás esto te ayude. Que esto no encubra una relación de muchos a muchos, pero es bastante útil cuando quieres enviar tu modelo en formato json.

def serial_model(modelobj): opts = modelobj._meta.fields modeldict = model_to_dict(modelobj) for m in opts: if m.is_relation: foreignkey = getattr(modelobj, m.name) if foreignkey: try: modeldict[m.name] = serial_model(foreignkey) except: pass return modeldict


Si está dispuesto a definir su propio método to_dict como lo sugirió @karthiker, eso reduce el problema a un problema de conjuntos.

>>># Returns a set of all keys excluding editable = False keys >>>dict = model_to_dict(instance) >>>dict {u''id'': 1L, ''reference1'': 1L, ''reference2'': [1L], ''value'': 1} >>># Returns a set of editable = False keys, misnamed foreign keys, and normal keys >>>otherDict = SomeModel.objects.filter(id=instance.id).values()[0] >>>otherDict {''created'': datetime.datetime(2014, 2, 21, 4, 38, 51, tzinfo=<UTC>), u''id'': 1L, ''reference1_id'': 1L, ''value'': 1L, ''value2'': 2L}

Necesitamos eliminar las claves externas mal etiquetadas de otherDict .

Para hacer esto, podemos usar un bucle que crea un nuevo diccionario que tiene todos los elementos excepto aquellos con guiones bajos en ellos. O bien, para ahorrar tiempo, podemos agregarlos al dict original ya que los diccionarios son solo conjuntos bajo el capó.

>>>for item in otherDict.items(): ... if "_" not in item[0]: ... dict.update({item[0]:item[1]}) ... >>>

Por lo tanto, nos quedamos con el siguiente dict :

>>>dict {''created'': datetime.datetime(2014, 2, 21, 4, 38, 51, tzinfo=<UTC>), u''id'': 1L, ''reference1'': 1L, ''reference2'': [1L], ''value'': 1, ''value2'': 2L}

Y tú solo regresas eso.

En el lado negativo, no puede usar guiones bajos en sus editable = nombres de campos falsos. Por el lado positivo, esto funcionará para cualquier conjunto de campos donde los campos creados por el usuario no contengan guiones bajos.

--EDITAR--

Esta no es la mejor manera de hacerlo, pero podría funcionar como una solución temporal hasta que se encuentre un método más directo.

Para el ejemplo a continuación, dict se formaría basado en model_to_dict y otherDict se formaría por el método de valores de filtro. Lo habría hecho con los modelos en sí, pero no puedo hacer que mi máquina acepte otro modelo.

>>> import datetime >>> dict = {u''id'': 1, ''reference1'': 1, ''reference2'': [1], ''value'': 1} >>> otherDict = {''created'': datetime.datetime(2014, 2, 21, 4, 38, 51), u''id'': 1, ''reference1_id'': 1, ''value'': 1, ''value2'': 2} >>> for item in otherDict.items(): ... if "_" not in item[0]: ... dict.update({item[0]:item[1]}) ... >>> dict {''reference1'': 1, ''created'': datetime.datetime(2014, 2, 21, 4, 38, 51), ''value2'': 2, ''value'': 1, ''id'': 1, ''reference2'': [1]} >>>

Eso debería ponerlo en un estadio aproximado de la respuesta a su pregunta, espero.


just vars(obj) , indicará los valores completos del objeto

{''_file_data_cache'': <FileData: Data>, ''_state'': <django.db.models.base.ModelState at 0x7f5c6733bad0>, ''aggregator_id'': 24, ''amount'': 5.0, ''biller_id'': 23, ''datetime'': datetime.datetime(2018, 1, 31, 18, 43, 58, 933277, tzinfo=<UTC>), ''file_data_id'': 797719, }