Як ви серіалізуєте примірник моделі в Django?


157

Існує багато документації про те, як серіалізувати Model QuerySet, але як ви просто серіалізуєте JSON поля модельного екземпляра?


Хоча це виглядає так, що ви можете серіалізувати набір запитів з 1 об’єкта, ви не можете використовувати класи django.coreдля цього. Якась конкретна причина не використовувати серіалізувати набір запитів?
Джек М.

1
Серіалізатор запитів намотає результат у ще два шари, ніж це доводиться. Тому вам доведеться робити дані [0] .fields.name замість data.name.
Джейсон Кріста

Це те, що я думав. Я зіткнувся з тим самим питанням, коли писав інтерфейс GWT для бекенда джанго. Схоже, Девід може на щось потрапити.
Джек М.

Відповіді:


241

Ви можете легко скористатися списком, щоб обернути необхідний об’єкт, і це все, що потрібно серіалізаторів django, щоб правильно його серіалізувати, наприклад:

from django.core import serializers

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

9
Але у відповідь вам потрібно проіндексувати нульовий елемент об’єкта JSON, щоб дістатися до серіалізованого об'єкта. Просто щось зауважити.
Давор Лучич

10
А як щодо серіалізації всіх посилаються об'єктів разом із кореневим об’єктом?
paweloque

1
Ви не хочете [0]в кінці останнього рядка, як @DavorLucic запропонував? І немає необхідності в коду в списку буквально (для любові до PEP8;).
варильні панелі

Десеріалізація також вимагає додаткового кроку; см stackoverflow.com/a/29550004/2800876
Zags

5
Це не спрацювало для мене. Джанго кидає об'єкт AttributeError 'tuple' не має атрибута '_meta'
adamF

71

Якщо ви маєте справу зі списком модельних примірників, що найкраще ви можете використовувати serializers.serialize(), він ідеально відповідає вашим потребам.

Однак вам доведеться зіткнутися з проблемою, намагаючись серіалізувати один об'єкт, а не listоб'єкти. Таким чином, щоб позбутися різних хакків, просто використовуйте Django model_to_dict(якщо я не помиляюсь, serializers.serialize()покладається і на нього):

from django.forms.models import model_to_dict

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

Вам зараз потрібен лише один прямий json.dumpsдзвінок, щоб серіалізувати його на json:

import json
serialized = json.dumps(dict_obj)

Це воно! :)


6
це не вдається з полями UUID, має бути зрозуміло інакше
Елвін

2
провалюється з datetimeполями. Вирішили це таким чином, json.loads(serialize('json', [obj]))[0]поки серіалізаторdjango.core.serializers.serialize
Lal Zada

43

Щоб уникнути обгортки масиву, видаліть його, перш ніж повернути відповідь:

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

Я знайшов і цей цікавий пост на цю тему:

http://timsaylor.com/convert-django-model-in вещества-to-dic Dictionary

Він використовує django.forms.models.model_to_dict, який виглядає як ідеальний інструмент для роботи.


9
якщо це найкращий спосіб серіалізувати єдину модель в джанго, то це жахливо, оскільки не потрібно десеріалізувати json і серіалізувати його назад.
Герберт

@ Гербер, можливо. Але є. Якщо у вас є кращий шлях, я все вухо. Це не повинно мати багато практичних недоліків, оскільки вилучення та перезапис кодування одного об'єкта не повинен бути таким ресурсоємним. Перетворіть його на помічну функцію або розширте / змішайте свої об’єкти як новий метод, якщо ви хочете приховати жах.
Джуліан

1
Приховувати жах - це не проблема, а може бути, навіть не це рішення; що мене дивує, що це найкращий спосіб джанго.
Герберт

14

Є хороша відповідь на це, і я дивуюсь, що це не було згадано. За допомогою декількох рядків ви можете обробляти дати, моделі та все інше.

Створіть спеціальний кодер, який може працювати з моделями:

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)

Тепер використовуйте його, коли ви використовуєте json.dumps

json.dumps(data, cls=ExtendedEncoder)

Тепер моделі, дати і все можна серіалізувати, і це не повинно бути в масиві або серіалізовано і несеріалізоване. Все, що у вас є, що є на замовлення, можна просто додати до defaultметоду.

Ви навіть можете використовувати рідний JsonResponse рідного Django таким чином:

from django.http import JsonResponse

JsonResponse(data, encoder=ExtendedEncoder)
``

3
Це рішення просте і елегантне. Кодер можна використовувати як з методами, так json.dumpsі з json.dumpметодами. Таким чином, вам не потрібно змінювати робочий процес програми, оскільки ви використовуєте власні об'єкти або додаєте інший виклик методу перед переходом у json. Просто додайте свій код перетворення в кодер і ви готові до роботи.
Клаудіу

11

Це здається, що те, що ви запитуєте, передбачає серіалізацію структури даних екземпляра моделі Django для взаємодії. Інші плакати правильні: якби ви хотіли використовувати серіалізовану форму з додатком python, який може запитувати базу даних через api Джанго, тоді ви хочете серіалізувати набір запитів з одним об’єктом. Якщо, з іншого боку, те, що вам потрібно, це спосіб повторно надути екземпляр моделі де-небудь ще, не торкаючись бази даних або не використовуючи Django, то вам доведеться трохи попрацювати.

Ось що я роблю:

По-перше, я використовую demjsonдля конверсії. Це сталося саме те, що я знайшов першим, але це може бути не найкращим. Моя реалізація залежить від однієї з її особливостей, але мають бути схожі способи з іншими перетворювачами.

По-друге, застосуйте json_equivalentметод на всіх моделях, які вам можуть знадобитися серіалізувати. Це магічний метод для demjson, але це, напевно, те, про що ви хочете подумати, незалежно від того, яку реалізацію ви виберете. Ідея полягає в тому, що ви повертаєте об'єкт, який безпосередньо перетворюється в json(тобто масив або словник). Якщо ви дійсно хочете зробити це автоматично:

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

Це не буде для вас корисним, якщо ви не маєте повністю рівну структуру даних (ні ForeignKeys, лише цифри та рядки в базі даних тощо). В іншому випадку слід серйозно задуматися над правильним способом втілення цього методу.

По-третє, дзвоніть, demjson.JSON.encode(instance)і у вас є те, що ви хочете.


Я ще не пробував код, але просто хотів вказати на нього деякі помилки. Це instance._meta.get_all_field_names (), а getattribute - це функція, яка повинна мати (), а не [].
Джейсон Кріста

окрім FK, це не працюватиме для полів дати (якщо тільки в demjson.JSON.encode не є магія)
Skylar Saveland

8

Якщо ви запитуєте, як серіалізувати один об'єкт з моделі, і ви знаєте, що збираєтесь отримати лише один об’єкт у наборі запитів (наприклад, за допомогою object.get), тоді використовуйте щось на кшталт:

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

який би отримав щось із форми:

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

Але це позбавить усіх квадратних дужок, а не лише зовнішніх. Краще? "o = s [1: -1]"?
Юліан

5

Я вирішив цю проблему, додавши метод серіалізації до моєї моделі:

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

Ось багатослівний еквівалент для тих, хто не проти однолінійки:

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 - упорядкований список модельних полів, доступ до яких можна отримати з екземплярів та з самої моделі.


3
Хоча спочатку ця ідея може здатися хорошою, слід зазначити, що використання цього підходу є наслідками. Ви прив’язуєте до своєї моделі один конкретний вихід серіалізації.
Йонас Гейрегат

@JonasGeiregat, оскільки цей метод визначений на основі моделі, що не так у підході? На жаль, це здається єдиним способом повернути об’єкт json, який містить і поля, і первинний ключ екземпляра.
допатраман

5

Ось моє рішення для цього, яке дозволяє легко налаштувати JSON, а також впорядкувати відповідні записи

По-перше, реалізуйте метод на моделі. Я закликаю, jsonале ви можете називати його все, що завгодно, наприклад:

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

Тоді в погляді я роблю:

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

У Python 3 це стаєcar.json()
T.Coutlakis

4

Використовуйте список, це вирішить проблему

Крок 1:

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

Крок 2:

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

Крок 3:

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

Це здається, що він все ще буде серіалізувати як масив json (об'єктів), а не голий об’єкт, про що запитував ОП. Так, це не відрізняється від методу регулярної серіалізації.
Джуліан

1
Це не вдасться з помилкою серіалізації JSON. Об'єкти запитів не піддаються серіалізації
допатраман

4

Для серіалізації та десеріалізації використовуйте наступне:

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

Я отримую 'об’єкт генератора не має атрибута' next '. Будь-яка ідея?
користувач2880391

1
@ user2880391 Я оновив це для Python 3. Чи це виправлено?
Загс

4

.values() це те, що мені потрібно було перетворити екземпляр моделі в JSON.

.values ​​() документація: https://docs.djangoproject.com/en/3.0/ref/models/querysets/#values

Приклад використання в моделі під назвою Project .

Примітка: я використовую 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])

Результат з дійсного ідентифікатора:

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

3

Якщо ви хочете повернути клієнту об'єкт однієї моделі як json відповідь , ви можете зробити це просте рішення:

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

Використовуйте Django Serializer з pythonформатом,

from django.core import serializers

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

У чому різниця між форматом jsonі pythonформою?

jsonФормат буде повертати результат , як strтоді pythonбуде повертати результат в будь-якому listабоOrderedDict


це занадто прикро
JPG

Я отримую OrderedDict, неdict
user66081

1

Здається, ви не можете серіалізувати екземпляр, вам доведеться серіалізувати QuerySet одного об’єкта.

from django.core import serializers
from models import *

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

У мене закінчується svnвипуск джанго, тому цього може бути не в попередніх версіях.


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

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

Я повертаю дикту про свою інстанцію

тому воно повертає щось на зразок {'field1': value, "field2": value, ....}


це порушиться:TypeError: <django.db.models.base.ModelState object at 0x7f2b3bf62310> is not JSON serializable
Хуліо

1

як щодо цього:

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

або виключити все, що ви не хочете.


0

Усі ці відповіді були трохи хиткими в порівнянні з тим, що я б очікував від фреймворка, найпростішого методу, я думаю, на сьогоднішній день, якщо ви використовуєте решту фрейму:

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

При цьому використовується Serializer безпосередньо, поважаючи визначені вами поля, а також будь-які асоціації тощо.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.