Làm thế nào để bạn tuần tự hóa một ví dụ mô hình trong Django?


157

Có rất nhiều tài liệu về cách tuần tự hóa Bộ truy vấn mô hình nhưng làm thế nào để bạn chỉ tuần tự hóa thành JSON các trường của Sơ đồ mô hình?


Mặc dù có vẻ như bạn có thể tuần tự hóa một bộ truy vấn của 1 đối tượng, bạn không thể sử dụng các lớp django.coređể làm điều này. Bất kỳ lý do cụ thể không sử dụng tuần tự hóa bộ truy vấn?
Jack M.

1
Bộ tuần tự truy vấn bao bọc kết quả trong hai lớp nhiều hơn so với nó. Vì vậy, bạn phải làm dữ liệu [0] .fields.name thay vì data.name.
Jason Christa

Đó là những gì tôi nghĩ. Tôi gặp vấn đề tương tự khi tôi đang viết giao diện GWT cho phần phụ trợ django. Có vẻ như David có thể đang ở một cái gì đó.
Jack M.

Câu trả lời:


240

Bạn có thể dễ dàng sử dụng một danh sách để bọc đối tượng cần thiết và đó là tất cả những gì serial serial django cần để tuần tự hóa nó một cách chính xác, ví dụ:

from django.core import serializers

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

9
Nhưng để đáp lại, bạn được yêu cầu lập chỉ mục phần tử 0 của đối tượng JSON để đến đối tượng được tuần tự hóa. Chỉ cần một cái gì đó để lưu ý.
Davor Lucic

10
Và làm thế nào về việc tuần tự hóa tất cả các đối tượng được tham chiếu cùng với đối tượng gốc?
paweloque

1
Bạn không muốn [0]ở cuối dòng cuối cùng của bạn, như @DavorLucic đã đề xuất? Và không cần dấu phẩy trong danh sách theo nghĩa đen của bạn (vì tình yêu của PEP8;).
hobs

Quá trình khử lưu huỳnh cũng đòi hỏi một bước bổ sung; xem stackoverflow.com/a/29550004/2800876
Zags

4
Điều này đã không làm việc cho tôi. Django ném đối tượng AttributionError 'tuple' không có thuộc tính '_meta'
adamF

71

Nếu bạn đang xử lý một danh sách các trường hợp mô hình tốt nhất bạn có thể làm là sử dụng serializers.serialize(), nó sẽ phù hợp với nhu cầu của bạn một cách hoàn hảo.

Tuy nhiên, bạn đang phải đối mặt với một vấn đề với cố gắng để serialize một đơn đối tượng, không phải là một listđối tượng. Bằng cách đó, để thoát khỏi các bản hack khác nhau, chỉ cần sử dụng Django model_to_dict(nếu tôi không nhầm, cũng serializers.serialize()dựa vào nó):

from django.forms.models import model_to_dict

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

Bây giờ bạn chỉ cần một json.dumpscuộc gọi thẳng để nối tiếp nó đến json:

import json
serialized = json.dumps(dict_obj)

Đó là nó! :)


6
điều này không thành công với các trường UUID, nên rõ ràng khác
Alvin

2
thất bại với datetimecác lĩnh vực. Giải quyết nó theo cách này json.loads(serialize('json', [obj]))[0]trong khi serializer làdjango.core.serializers.serialize
Lal Zada

43

Để tránh trình bao bọc mảng, hãy xóa nó trước khi bạn trả lời phản hồi:

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

Tôi cũng tìm thấy bài viết thú vị về chủ đề này:

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

Nó sử dụng django.forms.models.model_to_dict, có vẻ như là công cụ hoàn hảo cho công việc.


9
Nếu đây là cách tốt nhất để tuần tự hóa một mô hình duy nhất trong django, thì điều đó thật kinh khủng vì người ta không cần phải giải tuần tự hóa json và tuần tự hóa nó trở lại.
Herbert

@Herbert, có lẽ. Nhưng nó đây rồi. Nếu bạn có một cách tốt hơn, tôi là tất cả tai. Điều này không nên có nhiều nhược điểm thực tế vì tìm nạp và giải mã lại một đối tượng không nên sử dụng nhiều tài nguyên đó. Biến nó thành một hàm trợ giúp hoặc mở rộng / trộn lẫn với các đối tượng của bạn như một phương thức mới nếu bạn muốn che giấu nỗi kinh hoàng.
Julian

1
Che giấu nỗi kinh hoàng không phải là vấn đề và thậm chí có thể không phải là giải pháp này; Điều làm tôi ngạc nhiên là đây là cách tốt nhất để làm điều đó.
Herbert

13

Có một câu trả lời tốt cho điều này và tôi ngạc nhiên rằng nó đã không được đề cập. Với một vài dòng bạn có thể xử lý ngày, mô hình và mọi thứ khác.

Tạo một bộ mã hóa tùy chỉnh có thể xử lý các mô hình:

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)

Bây giờ sử dụng nó khi bạn sử dụng json.dumps

json.dumps(data, cls=ExtendedEncoder)

Bây giờ các mô hình, ngày tháng và mọi thứ có thể được tuần tự hóa và nó không phải nằm trong một mảng hoặc được tuần tự hóa và không được phân loại. Bất cứ điều gì bạn có đó là tùy chỉnh chỉ có thể được thêm vào defaultphương thức.

Bạn thậm chí có thể sử dụng JsonResponse bản địa của Django theo cách này:

from django.http import JsonResponse

JsonResponse(data, encoder=ExtendedEncoder)
``

3
Giải pháp này đơn giản và thanh lịch. Bộ mã hóa có thể được sử dụng với cả hai json.dumpsjson.dumpphương thức. Bằng cách này, bạn không cần thay đổi quy trình làm việc của ứng dụng vì bạn sử dụng các đối tượng tùy chỉnh hoặc thêm một cuộc gọi phương thức khác trước khi chuyển đổi sang json. Chỉ cần thêm mã chuyển đổi của bạn trong bộ mã hóa và bạn đã sẵn sàng để đi.
Claudiu

11

Nghe có vẻ giống như những gì bạn đang hỏi về việc tuần tự hóa cấu trúc dữ liệu của cá thể mô hình Django để có khả năng tương tác. Các áp phích khác là chính xác: nếu bạn muốn sử dụng biểu mẫu nối tiếp với ứng dụng python có thể truy vấn cơ sở dữ liệu thông qua api của Django, thì bạn sẽ muốn tuần tự hóa một bộ truy vấn với một đối tượng. Mặt khác, nếu bạn cần một cách để làm lại cá thể mô hình ở một nơi khác mà không cần chạm vào cơ sở dữ liệu hoặc không sử dụng Django, thì bạn có một chút việc phải làm.

Đây là những gì tôi làm:

Đầu tiên, tôi sử dụng demjsonđể chuyển đổi. Nó tình cờ là thứ tôi tìm thấy đầu tiên, nhưng nó có thể không phải là thứ tốt nhất. Việc triển khai của tôi phụ thuộc vào một trong các tính năng của nó, nhưng nên có những cách tương tự với các bộ chuyển đổi khác.

Thứ hai, thực hiện một json_equivalentphương thức trên tất cả các mô hình mà bạn có thể cần được tuần tự hóa. Đây là một phương pháp kỳ diệu cho demjson, nhưng có lẽ đó là điều bạn sẽ muốn nghĩ về bất kể bạn chọn cách triển khai nào. Ý tưởng là bạn trả về một đối tượng có thể chuyển đổi trực tiếp thành json(tức là một mảng hoặc từ điển). Nếu bạn thực sự muốn làm điều này tự động:

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

Điều này sẽ không hữu ích cho bạn trừ khi bạn có cấu trúc dữ liệu hoàn toàn bằng phẳng (không ForeignKeys, chỉ có số và chuỗi trong cơ sở dữ liệu, v.v.). Nếu không, bạn nên nghiêm túc suy nghĩ về cách đúng đắn để thực hiện phương pháp này.

Thứ ba, gọi demjson.JSON.encode(instance)và bạn có những gì bạn muốn.


Tôi chưa thử mã nhưng tôi chỉ muốn chỉ ra một số lỗi trong đó. Đó là instance._meta.get_all_field_names () và getattribution là một hàm nên có () chứ không phải [].
Jason Christa

ngoài FK, điều này sẽ không hoạt động đối với các trường datetime (trừ khi có phép thuật trong demjson.JSON.encode)
Skylar Saveland

8

Nếu bạn đang hỏi làm thế nào để tuần tự hóa một đối tượng từ một mô hình và bạn biết rằng bạn sẽ chỉ nhận được một đối tượng trong bộ truy vấn (ví dụ: sử dụng object.get), thì hãy sử dụng một cái gì đó như:

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

mà sẽ giúp bạn có một cái gì đó của hình thức:

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

Nhưng điều đó sẽ loại bỏ tất cả các dấu ngoặc vuông, không chỉ những cái bên ngoài. Tốt hơn? "O = s [1: -1]"?
Julian

5

Tôi đã giải quyết vấn đề này bằng cách thêm một phương thức tuần tự hóa vào mô hình của mình:

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

Đây là verbose tương đương với những người không thích một lớp lót:

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 là một danh sách theo thứ tự các trường mô hình có thể được truy cập từ các thể hiện và từ chính mô hình đó.


3
Mặc dù ý tưởng ban đầu có vẻ tốt, nhưng người ta nên chỉ ra rằng có những hậu quả khi sử dụng phương pháp này. Bạn đang buộc một đầu ra tuần tự hóa cụ thể cho mô hình của bạn.
Jonas Geiregat

@JonasGeiregat vì phương pháp này được xác định trên cơ sở mô hình-mô hình, điều gì sai với cách tiếp cận? Đáng buồn thay, đây dường như là cách duy nhất để trả về một đối tượng json chứa cả các trường và khóa chính của thể hiện.
dopatraman

5

Đây là giải pháp của tôi cho việc này, cho phép bạn dễ dàng tùy chỉnh JSON cũng như sắp xếp các bản ghi liên quan

Trước hết thực hiện một phương pháp trên mô hình. Tôi gọi là jsonnhưng bạn có thể gọi nó là bất cứ điều gì bạn thích, ví dụ:

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

Sau đó, trong quan điểm tôi làm:

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

Trong Python 3, nó trở thànhcar.json()
T.Coutlakis

4

Sử dụng danh sách, nó sẽ giải quyết vấn đề

Bước 1:

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

Bước 2:

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

Bước 3:

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

Có vẻ như nó vẫn sẽ tuần tự hóa như một mảng json (của các đối tượng) chứ không phải là một vật thể trần trụi, đó là những gì OP yêu cầu. iow, điều này không khác gì so với phương thức tuần tự hóa thông thường.
Julian

1
Điều này sẽ thất bại với lỗi tuần tự hóa JSON. Các đối tượng truy vấn không được tuần tự hóa
dopatraman

4

Để tuần tự hóa và giải nén, sử dụng như sau:

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

Tôi đang nhận được 'đối tượng trình tạo không có thuộc tính' tiếp theo '. Bất kỳ ý tưởng?
dùng2880391

1
@ user2880391 Tôi đã cập nhật cái này cho Python 3. Cái đó có sửa được không?
Zags

4

.values() là những gì tôi cần để chuyển đổi một cá thể mô hình sang JSON.

Tài liệu .values ​​(): https://docs.djangoproject.com/en/3.0/ref/models/querysets/#values

Ví dụ sử dụng với một mô hình được gọi là Project .

Lưu ý: Tôi đang sử dụng 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])

Kết quả từ một id hợp lệ:

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

3

Nếu bạn muốn trả về đối tượng mô hình đơn dưới dạng phản hồi json cho máy khách, bạn có thể thực hiện giải pháp đơn giản này:

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

Sử dụng Django serializer với pythonđịnh dạng,

from django.core import serializers

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

Sự khác biệt giữa jsonpythonđịnh dạng là gì?

Các jsonđịnh dạng sẽ trả về kết quả như strtrong khi pythonsẽ trở lại kết quả trong một trong hai listhoặcOrderedDict


điều đó thật đáng tiếc
JPG

Tôi nhận được OrderedDict, không phảidict
user66081

1

Dường như bạn không thể tuần tự hóa một cá thể, bạn phải tuần tự hóa một Truy vấn của một đối tượng.

from django.core import serializers
from models import *

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

Tôi hết svnbản phát hành django, vì vậy điều này có thể không có trong các phiên bản trước.


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

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

Tôi trả lại bản chính của ví dụ của tôi

vì vậy, nó trả về một cái gì đó như {'field1': value, "field2": value, ....}


Điều này sẽ phá vỡ:TypeError: <django.db.models.base.ModelState object at 0x7f2b3bf62310> is not JSON serializable
Julio Marins

1

làm thế nào về cách này:

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

hoặc loại trừ bất cứ điều gì bạn không muốn.


0

Tất cả những câu trả lời này là một chút khó khăn so với những gì tôi mong đợi từ một khung công tác, phương pháp đơn giản nhất, tôi nghĩ cho đến nay, nếu bạn đang sử dụng khung còn lại:

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

Điều này sử dụng Trình tuần tự hóa trực tiếp, tôn trọng các trường bạn đã xác định trên đó, cũng như mọi liên kết, v.v.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.