¿Cómo se convierte un objeto Modelo Django en un dict con todos sus campos? Todo incluye idealmente claves externas y campos con editable=False
.
Déjame elaborar. Digamos que tengo un modelo de Django como el siguiente:
from django.db import models
class OtherModel(models.Model): pass
class SomeModel(models.Model):
normal_value = models.IntegerField()
readonly_value = models.IntegerField(editable=False)
auto_now_add = models.DateTimeField(auto_now_add=True)
foreign_key = models.ForeignKey(OtherModel, related_name="ref1")
many_to_many = models.ManyToManyField(OtherModel, related_name="ref2")
En la terminal, he hecho lo siguiente:
other_model = OtherModel()
other_model.save()
instance = SomeModel()
instance.normal_value = 1
instance.readonly_value = 2
instance.foreign_key = other_model
instance.save()
instance.many_to_many.add(other_model)
instance.save()
Quiero convertir esto al siguiente diccionario:
{'auto_now_add': datetime.datetime(2015, 3, 16, 21, 34, 14, 926738, tzinfo=<UTC>),
'foreign_key': 1,
'id': 1,
'many_to_many': [1],
'normal_value': 1,
'readonly_value': 2}
Preguntas con respuestas insatisfactorias:
Django: Convertir un conjunto completo de objetos de un Modelo en un solo diccionario
to_dict
y manejarlo de la manera que desee._meta
definiciones del modelo para encontrar los campos asociados con el modelo y recuperar sus valores en la instancia.Respuestas:
Hay muchas formas de convertir una instancia en un diccionario, con diversos grados de manejo de mayúsculas y de proximidad al resultado deseado.
1)
instance.__dict__
que vuelve
Este es, con mucho, el más simple, pero falta
many_to_many
,foreign_key
tiene un nombre incorrecto y tiene dos cosas adicionales no deseadas.2)
model_to_dict
que vuelve
Este es el único con
many_to_many
, pero le faltan los campos no editables.3)
model_to_dict(..., fields=...)
que vuelve
Esto es estrictamente peor que la
model_to_dict
invocación estándar .4)
query_set.values()
que vuelve
Este es el mismo resultado
instance.__dict__
pero sin los campos adicionales.foreign_key_id
todavía está mal ymany_to_many
todavía falta.5. Función personalizada
El código para django
model_to_dict
tenía la mayor parte de la respuesta. Eliminó explícitamente los campos no editables, por lo que al eliminar esa verificación y obtener los identificadores de claves foráneas para muchos o muchos campos, se obtiene el siguiente código que se comporta como se desea:Si bien esta es la opción más complicada, las llamadas
to_dict(instance)
nos dan exactamente el resultado deseado:6. Use serializadores
ModelSerialzer de Django Rest Framework le permite construir un serializador automáticamente a partir de un modelo.
devoluciones
Esto es casi tan bueno como la función personalizada, pero auto_now_add es una cadena en lugar de un objeto de fecha y hora.
Bonus Round: mejor impresión de modelos
Si desea un modelo django que tenga una mejor visualización de la línea de comandos de Python, haga que sus modelos sean de clase secundaria:
Entonces, por ejemplo, si definimos nuestros modelos como tales:
Llamar
SomeModel.objects.first()
ahora da una salida como esta:fuente
isinstance
prueba en la solución n. ° 5 (y la bonificación) aif f.many_to_many
.model_to_dict
, que usaisinstance
. No estoy seguro de por qué hicieron esta elección, pero puede haber una buena razón para ello (como lamany_to_many
propiedad que se introdujo en una versión posterior)@property
campos?Encontré una solución ordenada para llegar al resultado:
Supongamos que tiene un objeto modelo
o
:Solo llama:
fuente
¡La solución de @Zags fue hermosa!
Sin embargo, agregaría una condición para los campos de fecha para que sea compatible con JSON.
Ronda de bonificación
Si desea un modelo django que tenga una mejor visualización de la línea de comandos de Python, haga que sus modelos sean de la siguiente clase:
Entonces, por ejemplo, si definimos nuestros modelos como tales:
Llamar
SomeModel.objects.first()
ahora da una salida como esta:fuente
La forma más simple
Si su consulta es Model.Objects.get ():
get () devolverá una sola instancia para que pueda usarla directamente
__dict__
desde su instanciamodel_dict =
Model.Objects.get().__dict__
para filter () / all ():
all () / filter () devolverá una lista de instancias para que pueda usar
values()
para obtener una lista de objetos.valores_modelo = Model.Objects.all (). values ()
fuente
solo
vars(obj)
, indicará los valores completos del objetoPuedes agregar esto también
fuente
Actualizar
La nueva respuesta agregada publicada por @zags es más completa y elegante que la mía. Por favor, consulte esa respuesta en su lugar.
Original
Si está dispuesto a definir su propio método to_dict como sugirió @karthiker, entonces eso simplemente reduce este problema a un problema de conjuntos.
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. O, para ahorrar tiempo, simplemente podemos agregarlos al dict original ya que los diccionarios son solo conjuntos bajo el capó.
Así nos quedamos con el siguiente dict :
Y acabas de devolver eso.
En el lado negativo, no puede usar guiones bajos en sus nombres de campo editables = falsos. Por el lado positivo, esto funcionará para cualquier conjunto de campos donde los campos creados por el usuario no contengan guiones bajos.
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 siguiente ejemplo, dict se formaría en base a model_to_dict y otherDict se formaría mediante el método de valores de filtro. Habría hecho esto con los modelos mismos, pero no puedo hacer que mi máquina acepte otro modelo.
Espero que eso te ponga en un estadio aproximado de la respuesta a tu pregunta.
fuente
re
aquí. Si se trata de filtrar claves con guiones bajos, no se trata de un código correcto ni de un comportamiento correcto.re.match("_", "reference1_id")
devolucionesNone
y columnas legítimas en la base de datos pueden tener guiones bajos en sus nombres.editable=false
campos. Solo estaba tratando de proporcionar algo con lo que podría trabajar hasta que se pudiera entregar una solución más canónica."_" in string
lugar dere
en ese caso.in
lugar dere
.Muchas soluciones interesantes aquí. Mi solución fue agregar un método as_dict a mi modelo con una comprensión dict.
Como beneficio adicional, esta solución combinada con una comprensión de la lista sobre una consulta lo convierte en una buena solución si desea exportar sus modelos a otra biblioteca. Por ejemplo, volcar sus modelos en un marco de datos de pandas:
fuente
(no quise hacer el comentario)
Ok, realmente no depende de los tipos de esa manera. Puede que haya entendido mal la pregunta original aquí, así que perdóname si ese es el caso. Si creas serliazers.py, entonces creas clases que tienen metaclases.
Luego, cuando obtenga los datos en la clase de vista, puede:
Eso es más o menos lo que tengo en una gran variedad de lugares y devuelve un buen JSON a través del JSONRenderer.
Como dije, esto es cortesía de DjangoRestFramework, por lo que vale la pena investigarlo.
fuente
La forma más fácil es simplemente usar
pprint
, que está en Python baseEsto proporciona un resultado similar
json.dumps(..., indent = 4)
pero que maneja correctamente los tipos de datos extraños que podrían estar incrustados en la instancia de su modelo, comoModelState
yUUID
, etc.Probado en Python 3.7
fuente
Quizás esto te ayude. Que esto no encubra una relación de muchos a muchos, pero es bastante útil cuando desea enviar su modelo en formato json.
fuente
La mejor solución que hayas visto.
Convierta la instancia django.db.models.Model y todos los campos de funciones ForeignKey, ManyToManyField y @Property relacionados en dict.
https://gist.github.com/shuge/f543dc2094a3183f69488df2bfb51a52
fuente
La respuesta de @zags es exhaustiva y debería ser suficiente,
pero el método # 5 (que es el mejor IMO) arroja un error, así que mejoré la función auxiliar.Como el OP solicitó convertir los
many_to_many
campos en una lista de claves primarias en lugar de una lista de objetos, mejoré la función para que el valor de retorno ahora sea serializable JSON, al convertirdatetime
objetos enstr
ymany_to_many
objetos en una lista de id.fuente
concrete_fields
ym2m_fields
parecer idénticos, por lo que suponiendo que elm2m_fields
bucle tiene una aplicación incorrecta aquí.AttributeError: 'list' object has no attribute 'values_list'
que no pude encontrar la razón detrás de esto. Estoy usando Django 2.1.1field.value_from_object
y, como resultado, demodel_to_dict
. He actualizado la sección 5 de mi respuesta para reflejar esto.