Jak serializować instancję modelu w Django?

157

Istnieje wiele dokumentacji na temat serializacji modelu QuerySet, ale jak po prostu serializować do JSON pola wystąpienia modelu?

Jason Christa
źródło
Chociaż wygląda na to, że można serializować zestaw zapytań składający się z 1 obiektu, nie można do tego użyć klas z django.core. Czy jest jakiś szczególny powód, aby nie używać serializacji zestawu zapytań?
Jack M.
1
Serializator zestawu zapytań opakowuje wynik w dwie warstwy więcej niż to konieczne. Więc musisz zrobić data [0] .fields.name zamiast data.name.
Jason Christa,
Tak myślałem. Napotkałem ten sam problem, kiedy pisałem interfejs GWT dla zaplecza django. Wygląda na to, że David coś wpadł.
Jack M.

Odpowiedzi:

241

Możesz łatwo użyć listy do zawijania wymaganego obiektu i to wszystko, czego serializatory django potrzebują, aby go poprawnie serializować, np .:

from django.core import serializers

# assuming obj is a model instance
serialized_obj = serializers.serialize('json', [ obj, ])
xaralis
źródło
9
Ale w odpowiedzi musisz zindeksować zerowy element obiektu JSON, aby dostać się do zserializowanego obiektu. Coś do zapamiętania.
Davor Lucic
10
A co z serializacją wszystkich obiektów, do których istnieją odwołania, wraz z obiektem głównym?
paweloque
1
Nie chcesz [0]na końcu swojej ostatniej linii, jak zasugerował @DavorLucic? I nie ma potrzeby umieszczania przecinka na końcu listy dosłownie (z miłości do PEP8;).
płyty grzewcze
Deserializacja wymaga również dodatkowego kroku; patrz stackoverflow.com/a/29550004/2800876
Zags
5
To nie zadziałało dla mnie. Django zgłasza obiekt AttributeError 'tuple' nie ma atrybutu '_meta'
adamF
71

Jeśli masz do czynienia z listą instancji modelu, najlepsze, co możesz zrobić, to użyć serializers.serialize(), będzie idealnie pasować do twoich potrzeb.

Jednak napotkasz problem z próbą serializacji pojedynczego obiektu, a nie listobiektów. W ten sposób, aby pozbyć się różnych hacków, po prostu użyj Django model_to_dict(jeśli się nie mylę, też serializers.serialize()na tym polega):

from django.forms.models import model_to_dict

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

Teraz potrzebujesz tylko jednego bezpośredniego json.dumpswywołania, aby serializować go do json:

import json
serialized = json.dumps(dict_obj)

Otóż ​​to! :)

Pavel Daynyak
źródło
6
to się nie powiedzie z polami UUID, w przeciwnym razie powinno być jasne
Alvin
2
zawodzi z datetimepolami. Rozwiązałem to w ten sposób, json.loads(serialize('json', [obj]))[0]podczas gdy serializator jestdjango.core.serializers.serialize
Lal Zada
43

Aby uniknąć otoki tablicy, usuń ją przed zwróceniem odpowiedzi:

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

Znalazłem też interesujący wpis na ten temat:

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

Używa django.forms.models.model_to_dict, które wygląda na idealne narzędzie do pracy.

juliański
źródło
9
jeśli jest to najlepszy sposób serializacji pojedynczego modelu w django, to jest to okropne, ponieważ nie powinno być potrzeby deserializacji json i serializacji go z powrotem.
Herbert
@Herbert, być może. Ale oto ona. Jeśli masz lepszy sposób, mam wszystkie uszy. Nie powinno to mieć zbyt wielu praktycznych wad, ponieważ pobieranie i dekodowanie / ponowne kodowanie pojedynczego obiektu nie powinno wymagać tak dużej ilości zasobów. Zmień go w funkcję pomocniczą lub rozszerz / zmieszaj z obiektami jako nową metodę, jeśli chcesz ukryć horror.
Julian,
1
Ukrywanie horroru nie jest problemem, a może nawet takim rozwiązaniem; co mnie zaskakuje, to to, że jest to najlepszy sposób na zrobienie tego przez django.
Herbert
14

Jest na to dobra odpowiedź i jestem zaskoczony, że nie została o tym wspomniana. Za pomocą kilku wierszy możesz obsłużyć daty, modele i wszystko inne.

Utwórz niestandardowy koder, który obsługuje modele:

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)

Teraz użyj go, gdy używasz json.dumps

json.dumps(data, cls=ExtendedEncoder)

Teraz modele, daty i wszystko to można serializować i nie musi to być w tablicy ani serializowane i nieserializowane. Wszystko, co masz niestandardowe, można po prostu dodać dodefault metody.

Możesz nawet użyć natywnej JsonResponse Django w ten sposób:

from django.http import JsonResponse

JsonResponse(data, encoder=ExtendedEncoder)
``
kagronick
źródło
3
To rozwiązanie jest proste i eleganckie. Enkodera można używać zarówno z metodami, jak json.dumpsi json.dump. W ten sposób nie musisz zmieniać przepływu pracy aplikacji, ponieważ używasz niestandardowych obiektów lub dodajesz inne wywołanie metody przed konwersją na json. Po prostu dodaj swój kod konwersji w koderze i gotowe.
Claudiu,
11

Wygląda na to, że pytasz o serializowanie struktury danych instancji modelu Django w celu zapewnienia współdziałania. Inne plakaty są poprawne: jeśli chcesz, aby serializowany formularz był używany z aplikacją Pythona, która może wysyłać zapytania do bazy danych za pośrednictwem interfejsu API Django, chciałbyś serializować zestaw zapytań za pomocą jednego obiektu. Jeśli z drugiej strony potrzebujesz sposobu na ponowne napełnienie instancji modelu w innym miejscu bez dotykania bazy danych lub bez użycia Django, to masz trochę pracy do wykonania.

Oto co robię:

Najpierw używam demjson do konwersji. Tak się złożyło, że znalazłem to, co znalazłem jako pierwsze, ale może nie być najlepsze. Moja implementacja zależy od jednej z jego funkcji, ale powinny być podobne sposoby z innymi konwerterami.

Po drugie, zaimplementuj json_equivalentmetodę we wszystkich modelach, które mogą być potrzebne do serializacji. Jest to magiczna metoda demjson, ale prawdopodobnie będzie to coś, o czym będziesz chciał pomyśleć bez względu na wybraną implementację. Chodzi o to, że zwracasz obiekt, który jest bezpośrednio konwertowany na json(tj. Tablicę lub słownik). Jeśli naprawdę chcesz to zrobić automatycznie:

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

Nie będzie to pomocne, chyba że masz całkowicie płaską strukturę danych (nie ForeignKeys, tylko liczby i ciągi znaków w bazie danych itp.). W przeciwnym razie powinieneś poważnie pomyśleć o właściwym sposobie wdrożenia tej metody.

Po trzecie, zadzwoń demjson.JSON.encode(instance)i masz to, czego chcesz.

David Berger
źródło
Nie próbowałem jeszcze kodu, ale chciałem tylko wskazać kilka błędów w nim. To jest instance._meta.get_all_field_names (), a getattribute to funkcja, więc powinna mieć (), a nie [].
Jason Christa,
oprócz FK to nie zadziała dla pól datetime (chyba, że ​​w demjson.JSON.encode jest magia)
Skylar Saveland
8

Jeśli pytasz, jak serializować pojedynczy obiekt z modelu i wiesz , że dostaniesz tylko jeden obiekt w zestawie zapytań (na przykład za pomocą objects.get), użyj czegoś takiego:

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

co dałoby ci coś w formie:

{"pk": 1, "model": "polls.poll", "fields": {"pub_date": "2013-06-27T02:29:38.284Z", "question": "What's up?"}}
benlast
źródło
Ale to usunie wszystkie kwadratowe nawiasy, nie tylko zewnętrzne. Lepszy? „o = s [1: -1]”?
Julian
5

Rozwiązałem ten problem, dodając metodę serializacji do mojego modelu:

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

Oto rozwlekły odpowiednik dla tych, którzy mają awersję do jednowierszowych:

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 to uporządkowana lista pól modelu, do których można uzyskać dostęp z instancji oraz z samego modelu.

davidchambers
źródło
3
Chociaż na początku pomysł może wydawać się dobry, należy zwrócić uwagę, że zastosowanie takiego podejścia ma konsekwencje. Wiązujesz jeden określony wynik serializacji z modelem.
Jonas Geiregat
@JonasGeiregat, ponieważ ta metoda jest definiowana na podstawie modelu do modelu, co jest złego w tym podejściu? Niestety wydaje się, że jest to jedyny sposób na zwrócenie obiektu json, który zawiera zarówno pola, jak i klucz podstawowy instancji.
dopatraman
5

Oto moje rozwiązanie, które pozwala łatwo dostosować JSON, a także organizować powiązane rekordy

Najpierw zaimplementuj metodę na modelu. Dzwonię, jsonale możesz to nazwać, jak chcesz, np:

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

Następnie w widoku robię:

data = [car.json for car in Car.objects.all()]
return HttpResponse(json.dumps(data), content_type='application/json; charset=UTF-8', status=status)
DanH
źródło
W Pythonie 3 staje sięcar.json()
T. Coutlakis
4

Użyj listy, to rozwiąże problem

Krok 1:

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

Krok 2:

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

Krok 3:

 return HttpResponse(json.dumps(result), content_type = "application/json")
niran
źródło
Wygląda na to, że nadal będzie serializowany jako tablica json (obiektów), a nie nagi obiekt, o co pytał OP. iow, to nie różni się od zwykłej metody serializacji.
Julian,
1
To się nie powiedzie z powodu błędu serializacji JSON. Obiekty Queryset nie są serializowane
dopatraman
4

Aby serializować i deserialze, użyj następującego:

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
źródło
Otrzymuję „obiekt generatora nie ma atrybutu„ następny ”. Dowolny pomysł?
user2880391
1
@ user2880391 Zaktualizowałem to dla Pythona 3. Czy to naprawi?
Zags,
4

.values() jest tym, czego potrzebowałem, aby przekonwertować instancję modelu na JSON.

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

Przykładowe użycie z modelem o nazwie Project .

Uwaga: używam 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])

Wynik z prawidłowego identyfikatora:

{
    "id": 47,
    "title": "Project Name",
    "description": "",
    "created_at": "2020-01-21T18:13:49.693Z",
}
Richard
źródło
3

Jeśli chcesz zwrócić pojedynczy obiekt modelu jako odpowiedź json do klienta, możesz zrobić to proste rozwiązanie:

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
źródło
2

Użyj Django Serializer z pythonformatem,

from django.core import serializers

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

Jaka jest różnica między jsonipython formatem ?

jsonFormat zwróci wynik jako strnatomiast pythonzwróci wynik w jednej listlubOrderedDict

JPG
źródło
to zbyt niefortunne
JPG
I dostać OrderedDict, niedict
user66081
1

Wydaje się, że nie można serializować instancji, musiałbyś serializować QuerySet jednego obiektu.

from django.core import serializers
from models import *

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

Skończyło mi się svnwydanie django, więc może nie być we wcześniejszych wersjach.

Jack M.
źródło
1
ville = UneVille.objects.get(nom='lihlihlihlih')
....
blablablab
.......

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

Zwracam dyktando mojej instancji

więc zwraca coś takiego jak {'pole1': wartość, "pole2": wartość, ....}


źródło
to się zepsuje:TypeError: <django.db.models.base.ModelState object at 0x7f2b3bf62310> is not JSON serializable
Julio Marins
1

co powiesz na ten sposób:

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

lub wyklucz wszystko, czego nie chcesz.

Reorx
źródło
0

Wszystkie te odpowiedzi były trochę zepsute w porównaniu z tym, czego oczekiwałbym od frameworka, myślę, że najprostsza metoda, jeśli używasz reszty:

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

Używa to Serializatora bezpośrednio, z poszanowaniem pól, które zdefiniowałeś w nim, a także wszelkich skojarzeń itp.

iantbutler
źródło