Como você serializa uma instância de modelo no Django?

159

Há muita documentação sobre como serializar um modelo QuerySet, mas como você serializa para JSON os campos de uma instância de modelo?

Jason Christa
fonte
Embora pareça que você pode serializar um conjunto de consultas de 1 objeto, não é possível usar as classes de django.corepara fazer isso. Algum motivo específico para não usar serializar o conjunto de consultas?
Jack M.
1
O serializador queryset agrupa o resultado em mais duas camadas do que o necessário. Então você precisa fazer dados [0] .fields.name em vez de data.name.
21730 Jason Christa
Isso foi o que eu pensei. Corri para o mesmo problema quando estava escrevendo uma interface GWT para um back-end django. Parece que David pode estar interessado em alguma coisa.
Jack M.
possível duplicata de Django serializador para um objeto
sleepycal

Respostas:

242

Você pode facilmente usar uma lista para envolver o objeto necessário e é isso que os serializadores do django precisam para serializá-lo corretamente, por exemplo:

from django.core import serializers

# assuming obj is a model instance
serialized_obj = serializers.serialize('json', [ obj, ])
xaralis
fonte
9
Mas, em resposta, você é obrigado a indexar zero elemento do objeto JSON para chegar ao objeto serializado. Apenas algo a ser observado.
Davor Lucic,
10
E que tal serializar todos os objetos referenciados junto com o objeto raiz?
paweloque
1
Você não deseja [0]o final da sua última linha, como sugerido pelo @DavorLucic? E não há necessidade de vírgula à direita na lista (pelo amor ao PEP8;).
hobs
A desserialização também requer uma etapa extra; Veja stackoverflow.com/a/29550004/2800876
Zags
5
Isso não funcionou para mim. Django joga objeto AttributeError 'tuple' tem '_meta' nenhum atributo
adamF
71

Se você estiver lidando com uma lista de instâncias de modelo, o melhor que você pode fazer é usá- serializers.serialize()lo, ele atenderá perfeitamente à sua necessidade.

No entanto, você deve enfrentar um problema ao tentar serializar um único objeto, não um listde objetos. Dessa forma, para se livrar de diferentes hacks, basta usar o Django model_to_dict(se não me engano, também serializers.serialize()depende):

from django.forms.models import model_to_dict

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

Agora você só precisa de uma json.dumpsligação direta para serializar para json:

import json
serialized = json.dumps(dict_obj)

É isso aí! :)

Pavel Daynyak
fonte
6
esta falha com campos UUID, deve ser claro de outra forma
Alvin
2
falha com os datetimecampos. Resolvido desta maneira json.loads(serialize('json', [obj]))[0], enquanto serializer édjango.core.serializers.serialize
Lal Zada
43

Para evitar o wrapper de matriz, remova-o antes de retornar a resposta:

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

Encontrei este post interessante sobre o assunto também:

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

Ele usa django.forms.models.model_to_dict, que parece a ferramenta perfeita para o trabalho.

Julian
fonte
9
se esta é a melhor maneira de serializar um único modelo no django, isso é horrível, pois não é necessário desserializar o json e serializá-lo novamente.
Herbert
@ Herbert, talvez. Mas aí está. Se você tem um jeito melhor, sou todo ouvidos. Isso não deve ter muita desvantagem prática, pois buscar e descriptografar um único objeto não deve exigir muito recurso. Transforme-o em uma função auxiliar ou estenda / misture com seus objetos como um novo método, se você quiser esconder o horror.
Julian
1
Esconder o horror não é o problema e talvez nem essa solução; o que me surpreende é que essa é a melhor maneira de fazê-lo.
Herbert #
14

Há uma boa resposta para isso e estou surpreso por não ter sido mencionado. Com algumas linhas, você pode lidar com datas, modelos e tudo mais.

Crie um codificador personalizado que possa manipular 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)

Agora use-o quando você usa json.dumps

json.dumps(data, cls=ExtendedEncoder)

Agora, modelos, datas e tudo podem ser serializados e não precisam estar em uma matriz ou serializados e não serializados. Tudo o que você tem personalizado pode ser adicionado ao defaultmétodo.

Você pode até usar o JsonResponse nativo do Django desta maneira:

from django.http import JsonResponse

JsonResponse(data, encoder=ExtendedEncoder)
``
kagronick
fonte
3
Esta solução é simples e elegante. O codificador pode ser usado com ambos json.dumpse json.dumpmétodos. Dessa forma, você não precisa alterar o fluxo de trabalho do aplicativo porque usa objetos personalizados ou adiciona outra chamada de método antes da conversão para json. Basta adicionar seu código de conversão no codificador e você está pronto para começar.
Claudiu
11

Parece que o que você está perguntando envolve serializar a estrutura de dados de uma instância do modelo Django para interoperabilidade. Os outros pôsteres estão corretos: se você desejasse que o formulário serializado fosse usado com um aplicativo python que possa consultar o banco de dados via API do Django, você desejaria serializar um conjunto de consultas com um objeto. Se, por outro lado, o que você precisa é uma maneira de aumentar novamente a instância do modelo em outro lugar sem tocar no banco de dados ou sem usar o Django, então você tem um pouco de trabalho a fazer.

Aqui está o que eu faço:

Primeiro, eu uso demjsonpara a conversão. Foi o que encontrei primeiro, mas pode não ser o melhor. Minha implementação depende de um de seus recursos, mas deve haver maneiras semelhantes com outros conversores.

Segundo, implemente um json_equivalentmétodo em todos os modelos que você possa precisar serializar. Esse é um método mágico demjson, mas provavelmente é algo em que você vai querer pensar, independentemente da implementação escolhida. A idéia é que você retorne um objeto que seja diretamente conversível em json(ou seja, uma matriz ou dicionário). Se você realmente deseja fazer isso automaticamente:

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

Isso não será útil para você, a menos que você tenha uma estrutura de dados completamente plana (não ForeignKeys, apenas números e seqüências de caracteres no banco de dados, etc.). Caso contrário, você deve pensar seriamente sobre a maneira correta de implementar esse método.

Terceiro, ligue demjson.JSON.encode(instance)e você tem o que deseja.

David Berger
fonte
Ainda não tentei o código, mas só queria apontar alguns erros nele. É instance._meta.get_all_field_names () e getattribute é uma função que deve ter () e não [].
21712 Jason Christa
além de FK, isso não vai funcionar para data e hora campos (a menos que haja magia demjson.JSON.encode)
Skylar Saveland
8

Se você está perguntando como serializar um único objeto a partir de um modelo e sabe que só obterá um objeto no conjunto de consultas (por exemplo, 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")

o que lhe daria algo da forma:

{"pk": 1, "model": "polls.poll", "fields": {"pub_date": "2013-06-27T02:29:38.284Z", "question": "What's up?"}}
benlast
fonte
Mas isso removerá todos os colchetes, não apenas os de fora. Melhor? "o = s [1: -1]"?
Julian
5

Resolvi esse problema adicionando um método de serialização ao meu 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]]))

Aqui está o equivalente detalhado para aqueles que são avessos a one-liners:

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 é uma lista ordenada de campos de modelo que podem ser acessados ​​a partir de instâncias e do próprio modelo.

davidchambers
fonte
3
Embora a ideia possa parecer boa a princípio, deve-se ressaltar que há consequências no uso dessa abordagem. Você está vinculando uma saída de serialização específica ao seu modelo.
Jonas Geiregat
@JonasGeiregat, como esse método é definido modelo a modelo, o que há de errado com a abordagem? Infelizmente, essa parece ser a única maneira de retornar um objeto json que contém os campos e a chave primária da instância.
Dopatraman 7/05/19
5

Aqui está minha solução para isso, que permite personalizar facilmente o JSON e organizar registros relacionados

Primeiramente implemente um método no modelo. Eu chamo é jsonmas você pode chamá-lo como quiser, por exemplo:

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

Então, na exibição, eu faço:

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

Use a lista, resolverá o problema

Passo 1:

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

Passo 2:

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

Etapa 3:

 return HttpResponse(json.dumps(result), content_type = "application/json")
niran
fonte
Parece que ainda será serializado como uma matriz json (de objetos) e não como um objeto nu, e foi sobre isso que o OP perguntou. Agora, isso não é diferente do que o método de serialização regular.
Julian
1
Isso falhará com um erro de serialização JSON. Objetos Queryset não são serializado
dopatraman
4

Para serializar e desserializar, use o seguinte:

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
fonte
Estou recebendo 'objeto gerador não tem nenhum atributo' próximo '. Qualquer ideia?
user2880391
1
@ user2880391 Atualizei isso para o Python 3. Isso corrige?
Zags
4

.values() é o que eu precisava para converter uma instância de modelo em JSON.

Documentação .values ​​(): https://docs.djangoproject.com/en/3.0/ref/models/querysets/#values

Exemplo de uso com um modelo chamado Projeto .

Nota: Estou usando o 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 um ID válido:

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

Se você deseja retornar o objeto de modelo único como uma resposta json para um cliente, é possível fazer esta solução simples:

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
fonte
2

Use o Django Serializer com pythonformato,

from django.core import serializers

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

Qual a diferença entre jsone pythonformato?

O jsonformato retornará o resultado como strconsiderando pythonque retornará o resultado em um listouOrderedDict

JPG
fonte
isso é muito lamentável
JPG
Eu recebo OrderedDict, nãodict
user66081
1

Parece que você não pode serializar uma instância, seria necessário serializar um QuerySet de um objeto.

from django.core import serializers
from models import *

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

Fico sem o svnlançamento do django, portanto isso pode não estar nas versões anteriores.

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

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

Volto o ditado da minha instância

portanto, retorna algo como {'campo1': valor, "campo2": valor, ....}


fonte
isso vai quebrar:TypeError: <django.db.models.base.ModelState object at 0x7f2b3bf62310> is not JSON serializable
Julio Marins
1

que tal desta maneira:

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

ou excluir qualquer coisa que você não queira.

Reorx
fonte
0

Todas essas respostas foram um pouco hacky em comparação com o que eu esperaria de uma estrutura, o método mais simples, eu acho, de longe, se você estiver usando a estrutura restante:

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

Isso usa o serializador diretamente, respeitando os campos que você definiu nele, bem como quaisquer associações, etc.

iantbutler
fonte