Wie serialisiert man eine Modellinstanz in Django?


157

Es gibt eine Menge Dokumentation zum Serialisieren eines Model QuerySet, aber wie serialisieren Sie einfach die Felder einer Model Instance in JSON?


Es sieht zwar so aus, als könnten Sie eine Abfragemenge von 1 Objekt serialisieren, Sie können jedoch nicht die Klassen von verwenden django.core, um dies zu tun. Gibt es einen bestimmten Grund, das Abfrageset nicht zu serialisieren?
Jack M.

1
Der Queryset-Serializer umschließt das Ergebnis in zwei Ebenen mehr als nötig. Sie müssen also data [0] .fields.name anstelle von data.name ausführen.
Jason Christa

Das ist was ich dachte. Ich bin auf dasselbe Problem gestoßen, als ich eine GWT-Schnittstelle für ein Django-Backend geschrieben habe. Sieht so aus, als wäre David auf etwas aus.
Jack M.

Antworten:


240

Sie können das erforderliche Objekt einfach mit einer Liste umschließen, und das ist alles, was Django-Serialisierer benötigen, um es korrekt zu serialisieren, z.

from django.core import serializers

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

9
Als Antwort müssen Sie jedoch das Nullelement des JSON-Objekts indizieren, um zum serialisierten Objekt zu gelangen. Nur etwas zu beachten.
Davor Lucic

10
Und wie wäre es, alle referenzierten Objekte zusammen mit dem Stammobjekt zu serialisieren?
Paweloque

1
Willst du nicht [0]am Ende deiner letzten Zeile, wie @DavorLucic vorgeschlagen hat? Und in Ihrem Listenliteral muss kein Komma nachgestellt werden (aus Liebe zu PEP8;).
Kochfelder

Die Deserialisierung erfordert auch einen zusätzlichen Schritt. siehe stackoverflow.com/a/29550004/2800876
Zags

4
Das hat bei mir nicht funktioniert. Django wirft AttributeError 'Tupel' Objekt hat kein Attribut '_meta'
adamF

71

Wenn Sie mit einer Liste von Modellinstanzen arbeiten, die Sie am besten verwenden können serializers.serialize(), passt sie perfekt zu Ihren Anforderungen.

Sie haben jedoch ein Problem mit dem Versuch, ein einzelnes Objekt und kein Objekt zu serialisieren list. Um verschiedene Hacks loszuwerden, verwenden Sie einfach Djangos model_to_dict(wenn ich mich nicht irre, verlassen serializers.serialize()Sie sich auch darauf):

from django.forms.models import model_to_dict

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

Sie brauchen jetzt nur noch einen direkten json.dumpsAufruf, um es an json zu serialisieren:

import json
serialized = json.dumps(dict_obj)

Das ist es! :) :)


6
Dies schlägt mit UUID-Feldern fehl, sollte ansonsten klar sein
Alvin

2
schlägt mit datetimeFeldern fehl . Auf diese Weise gelöst, json.loads(serialize('json', [obj]))[0]während Serializer istdjango.core.serializers.serialize
Lal Zada

43

Um den Array-Wrapper zu vermeiden, entfernen Sie ihn, bevor Sie die Antwort zurückgeben:

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

Ich fand diesen interessanten Beitrag auch zu diesem Thema:

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

Es verwendet django.forms.models.model_to_dict, das wie das perfekte Werkzeug für den Job aussieht.


9
Wenn dies der beste Weg ist, ein einzelnes Modell in Django zu serialisieren, ist das schrecklich, da man den JSON nicht deserialisieren und zurück serialisieren muss.
Herbert

@Herbert vielleicht. Aber da ist es. Wenn Sie einen besseren Weg haben, bin ich ganz Ohr. Dies sollte keinen großen praktischen Nachteil haben, da das Abrufen und De / Re-Codieren eines einzelnen Objekts nicht so ressourcenintensiv sein sollte. Machen Sie es zu einer Hilfsfunktion oder erweitern / mischen Sie Ihre Objekte als neue Methode, wenn Sie den Horror verbergen möchten.
Julian

1
Das Entsetzen des Grauens ist nicht das Problem und vielleicht auch nicht diese Lösung; Was mich überrascht ist, dass dies Djangos beste Art ist, dies zu tun.
Herbert

13

Es gibt eine gute Antwort darauf und ich bin überrascht, dass es nicht erwähnt wurde. Mit ein paar Zeilen können Sie Daten, Modelle und alles andere verarbeiten.

Erstellen Sie einen benutzerdefinierten Encoder, der Modelle verarbeiten kann:

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)

Verwenden Sie es jetzt, wenn Sie json.dumps verwenden

json.dumps(data, cls=ExtendedEncoder)

Jetzt können Modelle, Daten und alles serialisiert werden und es muss nicht mehr in einem Array oder serialisiert und unserialisiert sein. Alles, was Sie haben, das benutzerdefiniert ist, kann einfach zur defaultMethode hinzugefügt werden.

Sie können Djangos native JsonResponse sogar folgendermaßen verwenden:

from django.http import JsonResponse

JsonResponse(data, encoder=ExtendedEncoder)
``

3
Diese Lösung ist einfach und elegant. Der Encoder kann sowohl verwendet werden , json.dumpsund json.dumpMethoden. Auf diese Weise müssen Sie den Workflow der Anwendung nicht ändern, da Sie benutzerdefinierte Objekte verwenden oder einen anderen Methodenaufruf hinzufügen, bevor Sie in json konvertieren. Fügen Sie einfach Ihren Konvertierungscode in den Encoder ein und Sie können loslegen.
Claudiu

11

Es hört sich so an, als würden Sie die Datenstruktur einer Django-Modellinstanz für die Interoperabilität serialisieren. Die anderen Poster sind korrekt: Wenn Sie möchten, dass das serialisierte Formular mit einer Python-Anwendung verwendet wird, die die Datenbank über Djangos API abfragen kann, möchten Sie ein Abfrageset mit einem Objekt serialisieren. Wenn Sie andererseits eine Möglichkeit benötigen, die Modellinstanz an einer anderen Stelle wieder aufzublasen, ohne die Datenbank zu berühren oder Django zu verwenden, müssen Sie ein wenig arbeiten.

Folgendes mache ich:

Zunächst verwende ich demjsonfür die Konvertierung. Es war zufällig das, was ich zuerst gefunden habe, aber es ist vielleicht nicht das Beste. Meine Implementierung hängt von einer ihrer Funktionen ab, aber es sollte ähnliche Möglichkeiten mit anderen Konvertern geben.

Zweitens implementieren Sie eine json_equivalentMethode für alle Modelle, die möglicherweise serialisiert werden müssen. Dies ist eine magische Methode demjson, aber wahrscheinlich möchten Sie darüber nachdenken, egal für welche Implementierung Sie sich entscheiden. Die Idee ist, dass Sie ein Objekt zurückgeben, in das direkt konvertiert werden kann json(dh ein Array oder ein Wörterbuch). Wenn Sie dies wirklich automatisch tun möchten:

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

Dies ist für Sie nur hilfreich, wenn Sie eine vollständig flache Datenstruktur haben (nein ForeignKeys, nur Zahlen und Zeichenfolgen in der Datenbank usw.). Andernfalls sollten Sie ernsthaft darüber nachdenken, wie Sie diese Methode richtig implementieren können.

Drittens rufen demjson.JSON.encode(instance)Sie an und Sie haben, was Sie wollen.


Ich habe den Code noch nicht ausprobiert, wollte aber nur auf einige Fehler hinweisen. Es ist instance._meta.get_all_field_names () und getattribute ist eine Funktion, sollte also () und nicht [] haben.
Jason Christa

Zusätzlich zu FK funktioniert dies nicht für Datums- / Uhrzeitfelder (es sei denn, in demjson.JSON.encode
steckt

8

Wenn Sie fragen, wie ein einzelnes Objekt aus einem Modell serialisiert werden soll und Sie wissen, dass Sie nur ein Objekt im Abfrageset erhalten (z. B. mithilfe von objects.get), verwenden Sie Folgendes:

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

das würde dir etwas von der Form bringen:

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

Aber das wird alle eckigen Klammern entfernen, nicht nur die äußeren. Besser? "o = s [1: -1]"?
Julian

5

Ich habe dieses Problem gelöst, indem ich meinem Modell eine Serialisierungsmethode hinzugefügt habe:

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

Hier ist das ausführliche Äquivalent für diejenigen, die Einzeiler ablehnen:

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 ist eine geordnete Liste von Modellfeldern, auf die von Instanzen und vom Modell selbst aus zugegriffen werden kann.


3
Obwohl die Idee zunächst gut erscheinen mag, sollte man darauf hinweisen, dass die Verwendung dieses Ansatzes Konsequenzen hat. Sie binden eine bestimmte Serialisierungsausgabe an Ihr Modell.
Jonas Geiregat

@ JonasGeiregat Da diese Methode von Modell zu Modell definiert ist, was ist mit dem Ansatz falsch? Leider scheint dies die einzige Möglichkeit zu sein, ein JSON-Objekt zurückzugeben, das sowohl die Felder als auch den Primärschlüssel der Instanz enthält.
Dopatraman

5

Hier ist meine Lösung dafür, mit der Sie den JSON einfach anpassen und zugehörige Datensätze organisieren können

Implementieren Sie zunächst eine Methode für das Modell. Ich rufe an, jsonaber du kannst es nennen, wie du willst, zB:

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

Dann mache ich in der Ansicht:

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 wird escar.json()
T.Coutlakis

4

Verwenden Sie Liste, es wird das Problem lösen

Schritt 1:

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

Schritt 2:

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

Schritt 3:

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

Dies scheint immer noch als JSON-Array (von Objekten) und nicht als nacktes Objekt zu serialisieren, worum OP gebeten hat. Dies unterscheidet sich jedoch nicht von der regulären Serialisierungsmethode.
Julian

1
Dies schlägt mit einem JSON-Serialisierungsfehler fehl. Queryset-Objekte sind nicht serialisierbar
Dopatraman

4

Verwenden Sie zum Serialisieren und Deserialisieren Folgendes:

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

Ich erhalte 'Generatorobjekt hat kein Attribut' next '. Irgendeine Idee?
user2880391

1
@ user2880391 Ich habe dies für Python 3 aktualisiert. Behebt das das Problem?
Zags

4

.values() ist das, was ich brauchte, um eine Modellinstanz in JSON zu konvertieren.

Dokumentation zu .values ​​(): https://docs.djangoproject.com/de/3.0/ref/models/querysets/#values

Beispiel für die Verwendung mit einem Modell namens Project .

Hinweis: Ich verwende das 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])

Ergebnis einer gültigen ID:

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

3

Wenn Sie das einzelne Modellobjekt als JSON-Antwort an einen Client zurückgeben möchten , können Sie diese einfache Lösung ausführen:

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

Verwenden Sie Django Serializer mit pythonFormat,

from django.core import serializers

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

Was ist der Unterschied zwischen jsonund pythonFormat?

Das jsonFormat gibt das Ergebnis zurück, strwährend pythondas Ergebnis entweder listoder zurückgegeben wirdOrderedDict


das ist zu unglücklich
JPG

Ich bekomme OrderedDictnichtdict
user66081

1

Es scheint nicht, dass Sie eine Instanz serialisieren können, sondern Sie müssen ein QuerySet eines Objekts serialisieren.

from django.core import serializers
from models import *

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

Ich habe svnkeine Django-Version mehr, daher ist dies möglicherweise nicht in früheren Versionen der Fall.


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

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

Ich gebe das Diktat meiner Instanz zurück

es gibt also so etwas wie {'field1': value, "field2": value, ....} zurück


Dies wird brechen:TypeError: <django.db.models.base.ModelState object at 0x7f2b3bf62310> is not JSON serializable
Julio Marins

1

Wie wäre es mit diesem Weg:

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

oder alles ausschließen, was Sie nicht wollen.


0

Alle diese Antworten waren ein wenig hackig im Vergleich zu dem, was ich von einem Framework erwarten würde, der einfachsten Methode, denke ich bei weitem, wenn Sie das restliche Framework verwenden:

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

Hierbei wird der Serializer direkt verwendet, wobei die darin definierten Felder sowie etwaige Zuordnungen usw. berücksichtigt werden.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.