Как вы сериализуете экземпляр модели в Django?

159

Существует много документации о том, как сериализовать Model QuerySet, но как просто сериализовать в JSON поля экземпляра модели?

Джейсон Криста
источник
Хотя, похоже, что вы можете сериализовать набор запросов из 1 объекта, вы не можете использовать классы from django.coreдля этого. Любая конкретная причина не использовать сериализовать набор запросов?
Джек М.
1
Сериализатор наборов запросов упаковывает результат в два слоя больше, чем нужно. Поэтому вы должны сделать data [0] .fields.name вместо data.name.
Джейсон Криста
Это то, о чем я думал. Я столкнулся с той же проблемой, когда писал интерфейс GWT для бэкэнда django. Похоже, Дэвид может быть на что-то.
Джек М.
возможный дубликат сериализатора Django для одного объекта
sleepycal

Ответы:

242

Вы можете легко использовать список, чтобы обернуть требуемый объект, и это все, что нужно сериализаторам django для правильной сериализации, например:

from django.core import serializers

# assuming obj is a model instance
serialized_obj = serializers.serialize('json', [ obj, ])
xaralis
источник
9
Но в ответ вы обязаны индексировать нулевой элемент объекта JSON, чтобы получить сериализованный объект. Просто кое-что отметить.
Давор Лючич
10
А как насчет сериализации всех ссылочных объектов вместе с корневым объектом?
Павелок
1
Разве вы не хотите [0]в конце своей последней строки, как предложено @DavorLucic? И нет необходимости в конце запятой в вашем списке буквально (ради любви к PEP8;).
варенье
Десериализация также требует дополнительного шага; см. stackoverflow.com/a/29550004/2800876
Zags
5
Это не сработало для меня. Django выбрасывает 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-instances-to-dictionaries

Он использует django.forms.models.model_to_dict, который выглядит как идеальный инструмент для работы.

юлианский
источник
9
если это лучший способ сериализации одной модели в django, то это ужасно, так как не нужно десериализовать json и сериализовать его обратно.
Герберт
@ Херберт, возможно. Но это так. Если у вас есть лучший способ, я все уши. Это не должно иметь большого практического недостатка, поскольку выборка и / или повторное кодирование одного объекта не должны быть настолько ресурсоемкими. Превратите это в вспомогательную функцию или добавьте / добавьте ваши объекты в качестве нового метода, если вы хотите скрыть ужас.
Джулиан
1
Сокрытие ужаса - это не проблема и, возможно, даже не это решение; Что меня удивляет, так это то, что это лучший способ сделать это в Django.
Герберт
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)
``
kagronick
источник
3
Это простое и элегантное решение. Кодировщик может использоваться как с методами, так json.dumpsи с другими json.dump. Таким образом, вам не нужно изменять рабочий процесс приложения, потому что вы используете пользовательские объекты или добавляете другой вызов метода до преобразования в json. Просто добавьте свой код преобразования в кодировщик, и вы готовы к работе.
Клаудиу
11

Похоже, что вы спрашиваете, включает в себя сериализацию структуры данных экземпляра модели Django для совместимости. Другие постеры верны: если вы хотите, чтобы сериализованная форма использовалась с приложением python, которое может запрашивать базу данных через API Django, то вы бы хотели сериализовать набор запросов с одним объектом. Если, с другой стороны, вам нужен способ заново наполнить экземпляр модели где-то еще, не касаясь базы данных или не используя 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

Если вы спрашиваете, как сериализовать отдельный объект из модели и знаете , что получите только один объект в наборе запросов (например, с помощью objects.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?"}}
benlast
источник
Но это лишит все квадратные скобки, а не только внешние. Лучше? "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 упорядоченный список полей модели, доступ к которому можно получить из экземпляров и из самой модели.

davidchambers
источник
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)
Danh
источник
В Python 3 это становитсяcar.json()
Т.Кутлакис
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")
Niran
источник
Похоже, что он по-прежнему сериализуется как массив json (объектов), а не как голый объект, о чем и спрашивал OP. ау, это ничем не отличается от обычного метода сериализации.
Джулиан
1
Это не удастся с ошибкой сериализации JSON. Объекты Queryset не сериализуемы
dopatraman
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
Zags
источник
Я получаю «объект генератора не имеет атрибута« следующий ». Любая идея?
user2880391
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))
flowfelis
источник
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
источник
это слишком прискорбно
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выпуск django, так что это может быть не так в более ранних версиях.

Джек М.
источник
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 напрямую, уважая поля, которые вы на нем определили, а также любые ассоциации и т. Д.

iantbutler
источник