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
¡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,
Si su consulta es Model.Objects.get ():
get () devolverá una sola instancia para que pueda dirigir el uso de
__dict__
desde su instanciamodel_dict =
Model.Objects.get().__dict__
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,
}