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?


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

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


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.


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

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.


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?"}}

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.


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)

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

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

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",
}

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

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


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.


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ść, ....}


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.


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.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.