¿Cómo se serializa una instancia de modelo en Django?

159

Hay mucha documentación sobre cómo serializar un Model QuerySet, pero ¿cómo se serializa a JSON los campos de una instancia de modelo?

Jason Christa
fuente
Si bien parece que puede serializar un conjunto de consultas de 1 objeto, no puede usar las clases de django.corepara hacer esto. ¿Alguna razón en particular para no usar serializar el conjunto de consultas?
Jack M.
1
El serializador de conjunto de consultas envuelve el resultado en dos capas más de lo necesario. Entonces debe hacer data [0] .fields.name en lugar de data.name.
Jason Christa el
Es lo que pensaba. Me encontré con el mismo problema cuando estaba escribiendo una interfaz GWT para un backend de django. Parece que David podría estar en algo.
Jack M.
posible duplicado del serializador Django para un objeto
sleepycal

Respuestas:

242

Puede usar fácilmente una lista para ajustar el objeto requerido y eso es todo lo que los serializadores de django necesitan para serializarlo correctamente, por ejemplo:

from django.core import serializers

# assuming obj is a model instance
serialized_obj = serializers.serialize('json', [ obj, ])
xaralis
fuente
9
Pero en respuesta, debe indexar el elemento cero del objeto JSON para llegar al objeto serializado. Solo algo a tener en cuenta.
Davor Lucic
10
¿Y qué hay de serializar todos los objetos referenciados junto con el objeto raíz?
Paweloque
1
¿No quieres [0]al final de tu última línea, como sugirió @DavorLucic? Y no hay necesidad de una coma final en su lista literal (por amor a PEP8;).
placas
La deserialización también requiere un paso adicional; ver stackoverflow.com/a/29550004/2800876
Zags
55
Esto no funcionó para mí. Django lanza el atributo AttributeError 'tuple' no tiene atributo '_meta'
adamF
71

Si se trata de una lista de instancias de modelo, lo mejor que puede hacer es usarla serializers.serialize(), se ajustará perfectamente a su necesidad.

Sin embargo, debe enfrentar un problema al intentar serializar un solo objeto, no uno listde los objetos. De esa manera, para deshacerte de diferentes hacks, solo usa Django model_to_dict(si no me equivoco, serializers.serialize()confía en él también):

from django.forms.models import model_to_dict

# assuming obj is your model instance
dict_obj = model_to_dict( obj )

Ahora solo necesita una json.dumpsllamada directa para serializarlo a json:

import json
serialized = json.dumps(dict_obj)

¡Eso es! :)

Pavel Daynyak
fuente
66
esto falla con los campos UUID, de lo contrario debería quedar claro
Alvin
2
falla con los datetimecampos. Lo resolvió de esta manera json.loads(serialize('json', [obj]))[0]mientras el serializador esdjango.core.serializers.serialize
Lal Zada
43

Para evitar el contenedor de matriz, elimínelo antes de devolver la respuesta:

import json
from django.core import serializers

def getObject(request, id):
    obj = MyModel.objects.get(pk=id)
    data = serializers.serialize('json', [obj,])
    struct = json.loads(data)
    data = json.dumps(struct[0])
    return HttpResponse(data, mimetype='application/json')

También encontré esta publicación interesante sobre el tema:

http://timsaylor.com/convert-django-model-instances-to-dictionaries

Utiliza django.forms.models.model_to_dict, que parece la herramienta perfecta para el trabajo.

Julian
fuente
9
Si esta es la mejor manera de serializar un solo modelo en django, entonces eso es horrible, ya que uno no debería necesitar deserializar el json y volver a serializarlo.
Herbert
@Herbert, tal vez. Pero ahí está. Si tienes una mejor manera, soy todo oídos. Esto no debería tener muchos inconvenientes prácticos, ya que recuperar y descodificar un solo objeto no debería ser tan intensivo en recursos. Conviértalo en una función auxiliar o extienda / mezcle con sus objetos como un nuevo método si desea ocultar el horror.
Julian
1
Ocultar el horror no es el problema y quizás ni siquiera esta solución; Lo que me sorprende es que esta es la mejor manera de hacerlo.
Herbert
14

Hay una buena respuesta para esto y me sorprende que no se haya mencionado. Con unas pocas líneas puedes manejar fechas, modelos y todo lo demás.

Cree un codificador personalizado que pueda manejar modelos:

from django.forms import model_to_dict
from django.core.serializers.json import DjangoJSONEncoder
from django.db.models import Model

class ExtendedEncoder(DjangoJSONEncoder):

    def default(self, o):

        if isinstance(o, Model):
            return model_to_dict(o)

        return super().default(o)

Ahora úselo cuando use json.dumps

json.dumps(data, cls=ExtendedEncoder)

Ahora los modelos, las fechas y todo se pueden serializar y no tiene que estar en una matriz o serializado y sin serializar. Todo lo que tenga personalizado puede agregarse al defaultmétodo.

Incluso puede usar la respuesta Json nativa de Django de esta manera:

from django.http import JsonResponse

JsonResponse(data, encoder=ExtendedEncoder)
``
kagronick
fuente
3
Esta solución es simple y elegante. El codificador puede utilizarse con ambos json.dumpsy json.dumpmétodos. De esta manera, no necesita cambiar el flujo de trabajo de la aplicación porque usa objetos personalizados o agrega otro método de llamada antes de convertir a json. Simplemente agregue su código de conversión en el codificador y estará listo para comenzar.
Claudiu el
11

Parece que lo que está preguntando implica la serialización de la estructura de datos de una instancia del modelo Django para la interoperabilidad. Los otros carteles son correctos: si desea que el formulario serializado se use con una aplicación de Python que puede consultar la base de datos a través de la API de Django, entonces desea serializar un conjunto de consultas con un objeto. Si, por otro lado, lo que necesita es una forma de volver a inflar la instancia del modelo en otro lugar sin tocar la base de datos o sin usar Django, entonces tiene un poco de trabajo por hacer.

Esto es lo que hago:

Primero, uso demjsonpara la conversión. Resultó ser lo que encontré primero, pero podría no ser el mejor. Mi implementación depende de una de sus características, pero debería haber formas similares con otros convertidores.

Segundo, implemente un json_equivalentmétodo en todos los modelos que pueda necesitar serializados. Este es un método mágico demjson, pero probablemente sea algo en lo que quieras pensar, sin importar la implementación que elijas. La idea es que devuelva un objeto que sea directamente convertible a json(es decir, una matriz o diccionario). Si realmente quieres hacer esto automáticamente:

def json_equivalent(self):
    dictionary = {}
    for field in self._meta.get_all_field_names()
        dictionary[field] = self.__getattribute__(field)
    return dictionary

Esto no será útil para usted a menos que tenga una estructura de datos completamente plana (no ForeignKeys, solo números y cadenas en la base de datos, etc.). De lo contrario, debe pensar seriamente en la forma correcta de implementar este método.

Tercero, llama demjson.JSON.encode(instance)y tienes lo que quieres.

David Berger
fuente
Todavía no he probado el código, pero solo quería señalar algunos errores. Es instance._meta.get_all_field_names () y getattribute es una función, por lo que debería tener () y no [].
Jason Christa el
además de FK, esto no funcionará para los campos de fecha y hora (a menos que haya magia en demjson.JSON.encode)
Skylar Saveland
8

Si está preguntando cómo serializar un solo objeto de un modelo y sabe que solo obtendrá un objeto en el conjunto de consultas (por ejemplo, usando objects.get), use algo como:

import django.core.serializers
import django.http
import models

def jsonExample(request,poll_id):
    s = django.core.serializers.serialize('json',[models.Poll.objects.get(id=poll_id)])
    # s is a string with [] around it, so strip them off
    o=s.strip("[]")
    return django.http.HttpResponse(o, mimetype="application/json")

que te daría algo de la forma:

{"pk": 1, "model": "polls.poll", "fields": {"pub_date": "2013-06-27T02:29:38.284Z", "question": "What's up?"}}
benlast
fuente
Pero eso eliminará todos los corchetes, no solo los exteriores. ¿Mejor? "o = s [1: -1]"?
Julian
5

Resolví este problema agregando un método de serialización a mi modelo:

def toJSON(self):
    import simplejson
    return simplejson.dumps(dict([(attr, getattr(self, attr)) for attr in [f.name for f in self._meta.fields]]))

Aquí está el equivalente detallado para aquellos aversos a las frases sencillas:

def toJSON(self):
    fields = []
    for field in self._meta.fields:
        fields.append(field.name)

    d = {}
    for attr in fields:
        d[attr] = getattr(self, attr)

    import simplejson
    return simplejson.dumps(d)

_meta.fields es una lista ordenada de campos de modelo a la que se puede acceder desde instancias y desde el modelo mismo.

davidchambers
fuente
3
Aunque la idea puede parecer buena al principio, uno debe señalar que el uso de este enfoque tiene consecuencias. Está vinculando una salida de serialización específica a su modelo.
Jonas Geiregat
@JonasGeiregat ya que este método se define de modelo a modelo, ¿qué tiene de malo el enfoque? Lamentablemente, esta parece ser la única forma de devolver un objeto json que contiene tanto los campos como la clave principal de la instancia.
dopatraman
5

Aquí está mi solución para esto, que le permite personalizar fácilmente el JSON y organizar registros relacionados

Primero implemente un método en el modelo. Llamo jsonpero puedes llamarlo como quieras, por ejemplo:

class Car(Model):
    ...
    def json(self):
        return {
            'manufacturer': self.manufacturer.name,
            'model': self.model,
            'colors': [color.json for color in self.colors.all()],
        }

Entonces en la vista que hago:

data = [car.json for car in Car.objects.all()]
return HttpResponse(json.dumps(data), content_type='application/json; charset=UTF-8', status=status)
DanH
fuente
En Python 3, se convierte encar.json()
T.Coutlakis
4

Use la lista, resolverá el problema

Paso 1:

 result=YOUR_MODELE_NAME.objects.values('PROP1','PROP2').all();

Paso 2:

 result=list(result)  #after getting data from model convert result to list

Paso 3:

 return HttpResponse(json.dumps(result), content_type = "application/json")
niran
fuente
Parece que todavía se serializará como una matriz json (de objetos), no como un objeto desnudo, que es lo que OP preguntó. ahora, esto no es diferente al método de serialización regular.
Julian
1
Esto fallará con un error de serialización JSON. Queryset objetos no son serializable
dopatraman
4

Para serializar y deserializar, use lo siguiente:

from django.core import serializers

serial = serializers.serialize("json", [obj])
...
# .next() pulls the first object out of the generator
# .object retrieves django object the object from the DeserializedObject
obj = next(serializers.deserialize("json", serial)).object
Zags
fuente
Estoy obteniendo 'objeto generador no tiene atributo' siguiente '. ¿Alguna idea?
user2880391
1
@ user2880391 He actualizado esto para Python 3. ¿Eso lo soluciona?
Zags
4

.values() es lo que necesitaba para convertir una instancia de modelo a JSON.

.values ​​() documentación: https://docs.djangoproject.com/en/3.0/ref/models/querysets/#values

Ejemplo de uso con un modelo llamado Proyecto .

Nota: estoy usando Django Rest Framework

    @csrf_exempt
    @api_view(["GET"])
    def get_project(request):
        id = request.query_params['id']
        data = Project.objects.filter(id=id).values()
        if len(data) == 0:
            return JsonResponse(status=404, data={'message': 'Project with id {} not found.'.format(id)})
        return JsonResponse(data[0])

Resultado de una identificación válida:

{
    "id": 47,
    "title": "Project Name",
    "description": "",
    "created_at": "2020-01-21T18:13:49.693Z",
}
Ricardo
fuente
3

Si desea devolver el objeto de modelo único como respuesta json a un cliente, puede hacer esta solución simple:

from django.forms.models import model_to_dict
from django.http import JsonResponse

movie = Movie.objects.get(pk=1)
return JsonResponse(model_to_dict(movie))
Flowfelis
fuente
2

Utilice Django Serializer con pythonformato,

from django.core import serializers

qs = SomeModel.objects.all()
serialized_obj = serializers.serialize('python', qs)

¿Cuál es la diferencia entre jsony pythonformato?

El jsonformato devolverá el resultado como strmientras pythonque devolverá el resultado en listoOrderedDict

JPG
fuente
eso es muy desafortunado
JPG
Obtengo OrderedDict, nodict
user66081
1

No parece que pueda serializar una instancia, tendría que serializar un QuerySet de un objeto.

from django.core import serializers
from models import *

def getUser(request):
    return HttpResponse(json(Users.objects.filter(id=88)))

Me quedé sin el svnlanzamiento de django, por lo que puede que no sea en versiones anteriores.

Jack M.
fuente
1
ville = UneVille.objects.get(nom='lihlihlihlih')
....
blablablab
.......

return HttpResponse(simplejson.dumps(ville.__dict__))

Devuelvo el dict de mi instancia

entonces devuelve algo como {'field1': value, "field2": value, ....}


fuente
esto se romperá:TypeError: <django.db.models.base.ModelState object at 0x7f2b3bf62310> is not JSON serializable
Julio Marins
1

¿qué tal esta manera:

def ins2dic(obj):
    SubDic = obj.__dict__
    del SubDic['id']
    del SubDic['_state']
return SubDic

o excluir cualquier cosa que no quieras.

Reorx
fuente
0

Todas estas respuestas fueron un poco extravagantes en comparación con lo que esperaría de un marco, el método más simple, creo que con mucho, si está utilizando el resto del marco:

rep = YourSerializerClass().to_representation(your_instance)
json.dumps(rep)

Esto utiliza el serializador directamente, respetando los campos que ha definido en él, así como cualquier asociación, etc.

iantbutler
fuente