Come si serializza un'istanza del modello in Django?


159

C'è molta documentazione su come serializzare un Model QuerySet ma come si fa a serializzare su JSON i campi di un'istanza del modello?


Anche se sembra che sia possibile serializzare un queryset di 1 oggetto, non è possibile utilizzare le classi django.coreper farlo. Qualche motivo particolare per non utilizzare serializzare il queryset?
Jack M.

1
Il serializzatore queryset racchiude il risultato in due più livelli di quanti ne debba fare. Quindi devi fare data [0] .fields.name invece di data.name.
Jason Christa,

È quello che pensavo. Mi sono imbattuto nello stesso problema quando stavo scrivendo un'interfaccia GWT per un backend di django. Sembra che David potrebbe essere su qualcosa.
Jack M.

Risposte:


242

Puoi facilmente usare un elenco per racchiudere l'oggetto richiesto ed è tutto ciò che serve ai serializzatori django per serializzarlo correttamente, ad es .:

from django.core import serializers

# assuming obj is a model instance
serialized_obj = serializers.serialize('json', [ obj, ])

9
Ma in risposta è necessario indicizzare l'elemento zero dell'oggetto JSON per raggiungere l'oggetto serializzato. Solo qualcosa da notare.
Assapora Lucic l'

10
E che ne dici di serializzare tutti gli oggetti referenziati insieme all'oggetto root?
paweloque,

1
Non vuoi [0]alla fine dell'ultima riga, come suggerito da @DavorLucic? E non c'è bisogno di trascinare la virgola nel tuo elenco letterale (per l'amore di PEP8;).
Piani cottura

La deserializzazione richiede anche un ulteriore passo; vedi stackoverflow.com/a/29550004/2800876
zag

5
Questo non ha funzionato per me. Django lancia l'attributo AttributoErrore oggetto 'tupla' non ha attributo '_meta'
adamF

71

Se hai a che fare con un elenco di istanze del modello che il meglio che puoi fare è usare serializers.serialize(), si adatterà perfettamente alle tue esigenze.

Tuttavia, devi affrontare un problema con il tentativo di serializzare un singolo oggetto, non uno listdi oggetti. In questo modo, per sbarazzarti di diversi hack, usa semplicemente Django model_to_dict(se non sbaglio, fai serializers.serialize()affidamento anche su di esso):

from django.forms.models import model_to_dict

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

Ora hai solo bisogno di una json.dumpschiamata diretta per serializzare su json:

import json
serialized = json.dumps(dict_obj)

Questo è tutto! :)


6
questo fallisce con i campi UUID, altrimenti dovrebbe essere chiaro
Alvin

2
fallisce con i datetimecampi. Risolto in questo modo json.loads(serialize('json', [obj]))[0]mentre il serializzatore èdjango.core.serializers.serialize
Lal Zada,

43

Per evitare il wrapper di array, rimuoverlo prima di restituire la risposta:

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

Ho trovato questo interessante post anche sull'argomento:

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

Utilizza django.forms.models.model_to_dict, che sembra lo strumento perfetto per il lavoro.


9
se questo è il modo migliore per serializzare un singolo modello in django, allora è orribile in quanto non dovrebbe essere necessario deserializzare il json e serializzarlo nuovamente.
Herbert,

@Herbert, forse. Ma eccolo qui. Se hai un modo migliore, sono tutto orecchie. Questo non dovrebbe avere molti aspetti negativi pratici in quanto il recupero e la decodifica / re-codifica di un singolo oggetto non dovrebbero richiedere molte risorse. Trasformalo in una funzione di aiuto o estendi / mescola con i tuoi oggetti come nuovo metodo se vuoi nascondere l'orrore.
Giuliano,

1
Nascondere l'orrore non è il problema e forse nemmeno questa soluzione; ciò che mi sorprende è che questo è il modo migliore per farlo di Django.
Herbert,

14

C'è una buona risposta per questo e sono sorpreso che non sia stato menzionato. Con poche righe puoi gestire date, modelli e tutto il resto.

Crea un codificatore personalizzato in grado di gestire i modelli:

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)

Ora usalo quando usi json.dumps

json.dumps(data, cls=ExtendedEncoder)

Ora i modelli, le date e tutto possono essere serializzati e non devono essere in un array o serializzati e non serializzati. Tutto ciò che hai personalizzato può essere semplicemente aggiunto al defaultmetodo.

Puoi anche usare JsonResponse nativo di Django in questo modo:

from django.http import JsonResponse

JsonResponse(data, encoder=ExtendedEncoder)
``

3
Questa soluzione è semplice ed elegante. L'encoder può essere utilizzato con entrambi json.dumpse json.dumpmetodi. In questo modo non è necessario modificare il flusso di lavoro dell'applicazione poiché si utilizzano oggetti personalizzati o si aggiunge un'altra chiamata di metodo prima della conversione in json. Aggiungi il tuo codice di conversione nel codificatore e sei pronto per partire.
Claudiu,

11

Sembra che ciò che stai chiedendo riguardi la serializzazione della struttura dei dati di un'istanza del modello Django per l'interoperabilità. Gli altri poster sono corretti: se si desidera che il modulo serializzato venga utilizzato con un'applicazione Python in grado di eseguire query sul database tramite l'API di Django, quindi si vorrebbe serializzare un queryset con un oggetto. Se, d'altra parte, quello che ti serve è un modo per gonfiare nuovamente l'istanza del modello da qualche altra parte senza toccare il database o senza usare Django, allora hai un po 'di lavoro da fare.

Ecco cosa faccio:

In primo luogo, io uso demjsonper la conversione. È successo per caso quello che ho trovato per primo, ma potrebbe non essere il migliore. La mia implementazione dipende da una delle sue caratteristiche, ma ci dovrebbero essere modi simili con altri convertitori.

In secondo luogo, implementare un json_equivalentmetodo su tutti i modelli che potrebbero essere necessari serializzati. Questo è un metodo magico per demjson, ma probabilmente è qualcosa a cui vorresti pensare, indipendentemente dall'implementazione che scegli. L'idea è di restituire un oggetto direttamente convertibile json(ovvero un array o un dizionario). Se vuoi davvero farlo automaticamente:

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

Questo non ti sarà utile a meno che tu non abbia una struttura di dati completamente piatta (no ForeignKeys, solo numeri e stringhe nel database, ecc.). Altrimenti, dovresti pensare seriamente al modo giusto di implementare questo metodo.

In terzo luogo, chiama demjson.JSON.encode(instance)e hai quello che vuoi.


Non ho ancora provato il codice ma volevo solo sottolineare alcuni errori in esso. È instance._meta.get_all_field_names () e getattribute è una funzione, quindi dovrebbe avere () e non [].
Jason Christa,

oltre a FK, questo non funzionerà per i campi datetime (a meno che non ci sia magia in demjson.JSON.encode)
Skylar Saveland

8

Se stai chiedendo come serializzare un singolo oggetto da un modello e sai che otterrai un solo oggetto nel queryset (ad esempio, usando objects.get), usa qualcosa come:

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

che ti darebbe qualcosa del modulo:

{"pk": 1, "model": "polls.poll", "fields": {"pub_date": "2013-06-27T02:29:38.284Z", "question": "What's up?"}}

Ma questo eliminerà tutte le parentesi quadre, non solo quelle esterne. Meglio? "o = s [1: -1]"?
Giuliano

5

Ho risolto questo problema aggiungendo un metodo di serializzazione al mio modello:

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

Ecco l'equivalente dettagliato per quelli avversi a una linea:

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 è un elenco ordinato di campi modello a cui è possibile accedere dalle istanze e dal modello stesso.


3
Sebbene l'idea possa sembrare buona all'inizio, si dovrebbe sottolineare che ci sono conseguenze sull'uso di questo approccio. Stai collegando un output di serializzazione specifico al tuo modello.
Jonas Geiregat,

@JonasGeiregat poiché questo metodo è definito da modello a modello, cosa c'è di sbagliato nell'approccio? Purtroppo questo sembra essere l'unico modo per restituire un oggetto json che contiene sia i campi che la chiave primaria dell'istanza.
dopatraman

5

Ecco la mia soluzione per questo, che ti consente di personalizzare facilmente JSON e di organizzare i record correlati

Innanzitutto implementare un metodo sul modello. Io chiamo è jsonma puoi chiamarlo come preferisci, ad esempio:

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

Quindi nella vista lo faccio:

data = [car.json for car in Car.objects.all()]
return HttpResponse(json.dumps(data), content_type='application/json; charset=UTF-8', status=status)

In Python 3 diventacar.json()
T.Coutlakis il

4

Usa l'elenco, risolverà il 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

Fase 3:

 return HttpResponse(json.dumps(result), content_type = "application/json")

Sembra che continuerà a serializzare come un array json (di oggetti) non un oggetto nudo, che è ciò di cui l'OP ha chiesto. iow, questo non è diverso dal normale metodo di serializzazione.
Giuliano

1
Ciò fallirà con un errore di serializzazione JSON. Gli oggetti Queryset non sono serializzabili
dopatraman

4

Per serializzare e deserializzare, utilizzare quanto segue:

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

Sto ottenendo "l'oggetto generatore non ha alcun attributo" successivo ". Qualche idea?
user2880391

1
@ user2880391 Ho aggiornato questo per Python 3. Lo risolve?
Zags il

4

.values() è ciò di cui avevo bisogno per convertire un'istanza del modello in JSON.

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

Esempio di utilizzo con un modello chiamato Progetto .

Nota: sto 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])

Risultato da un ID valido:

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

3

Se si desidera restituire l' oggetto modello singolo come risposta json a un client, è possibile eseguire questa semplice soluzione:

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

2

Usa Django Serializer con pythonformato,

from django.core import serializers

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

Qual è la differenza tra jsone il pythonformato?

Il jsonformato restituirà il risultato mentre strmentre pythonrestituirà il risultato in uno listoOrderedDict


è troppo sfortunato
JPG

Ottengo OrderedDict, nondict
user66081

1

Non sembra che tu possa serializzare un'istanza, dovresti serializzare un QuerySet di un oggetto.

from django.core import serializers
from models import *

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

Ho finito il svnrilascio di Django, quindi potrebbe non essere disponibile nelle versioni precedenti.


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

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

Restituisco il dict della mia istanza

quindi restituisce qualcosa come {'field1': value, "field2": value, ....}


questo si spezzerà:TypeError: <django.db.models.base.ModelState object at 0x7f2b3bf62310> is not JSON serializable
Julio Marins,

1

che ne dici di questo modo:

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

o escludi tutto ciò che non vuoi.


0

Tutte queste risposte sono state un po 'confuse rispetto a quello che mi aspetterei da un framework, il metodo più semplice, penso di gran lunga, se stai usando il framework del resto:

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

Questo utilizza direttamente il serializzatore, rispettando i campi che hai definito su di esso, nonché eventuali associazioni, ecc.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.