คุณจะจัดลำดับตัวอย่างรุ่นใน Django ได้อย่างไร


157

มีเอกสารจำนวนมากเกี่ยวกับวิธีการทำให้อนุกรม QuerySet เป็นแบบอนุกรม แต่คุณจะทำอนุกรมให้ JSON เป็นเขตข้อมูลของ Model Instance ได้อย่างไร


ในขณะที่ดูเหมือนว่าคุณสามารถทำให้เป็นชุดแบบสอบถามของ 1 วัตถุคุณไม่สามารถใช้ชั้นเรียนจากdjango.coreการทำเช่นนี้ มีเหตุผลใดที่จะไม่ใช้ทำให้ชุดแบบสอบถามเป็นอนุกรมหรือไม่
แจ็คเอ็ม

1
ตัวตั้งค่าคิวรีชุดเครื่องมือล้อมรอบผลลัพธ์ในเลเยอร์มากกว่าสองรายการ ดังนั้นคุณต้องทำ data [0] .fields.name แทน data.name
เจสันคริส

นั่นคือสิ่งที่ฉันคิดว่า. ฉันพบปัญหาเดียวกันนี้เมื่อฉันกำลังเขียนอินเตอร์เฟส GWT สำหรับแบ็กเอนด์ django ดูเหมือนว่าดาวิดอาจจะทำอะไรบางอย่าง
แจ็คเอ็ม

อาจเป็นไปได้ที่ซ้ำกันของDjango serializer สำหรับวัตถุเดียว
sleepycal

คำตอบ:


242

คุณสามารถใช้รายการเพื่อห่อวัตถุที่ต้องการและนั่นคือสิ่งที่ django serializers จำเป็นต้องทำให้เป็นอนุกรมอย่างถูกต้องเช่น:

from django.core import serializers

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

9
แต่ในการตอบสนองคุณจะต้องทำดัชนีองค์ประกอบศูนย์ของวัตถุ JSON เพื่อไปยังวัตถุที่ต่อเนื่องกัน สิ่งที่ควรทราบ
Davor Lucic

10
และวิธีการเกี่ยวกับการเป็นอันดับวัตถุทั้งหมดที่อ้างอิงพร้อมกับวัตถุราก?
Paweloque

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 ควรมีความชัดเจนเป็นอย่างอื่น
Alvin

2
ล้มเหลวกับdatetimeเขตข้อมูล แก้ไขด้วยวิธีนี้json.loads(serialize('json', [obj]))[0]ในขณะที่ serializer คือdjango.core.serializers.serialize
Lal Zada

43

เพื่อหลีกเลี่ยง wrapper อาร์เรย์ให้ลบออกก่อนที่คุณจะตอบกลับ:

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, นั่นเป็นสิ่งที่น่ากลัวอย่างยิ่งที่ไม่ควรต้องทำการ deserialize json และทำให้มันเป็นอนุกรม
เฮอร์เบิร์

@ เฮอร์เบิร์ตบางที แต่มันมี หากคุณมีวิธีที่ดีกว่าฉันหูทั้งหมด สิ่งนี้ไม่ควรมีข้อเสียในทางปฏิบัติเท่าที่ควรเนื่องจากการดึงและไม่เข้ารหัสวัตถุเดียวไม่ควรใช้ทรัพยากรมาก ทำให้มันเป็นฟังก์ชั่นผู้ช่วยหรือขยาย / มิกซ์กับวัตถุของคุณเป็นวิธีการใหม่หากคุณต้องการซ่อนความสยองขวัญ
Julian

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

3
วิธีนี้ง่ายและสง่างาม ตัวเข้ารหัสสามารถใช้กับทั้งสองjson.dumpsและjson.dumpวิธีการ วิธีนี้คุณไม่จำเป็นต้องเปลี่ยนเวิร์กโฟลว์ของแอปพลิเคชันเนื่องจากคุณใช้วัตถุที่กำหนดเองหรือเพิ่มการเรียกใช้เมธอดอื่นก่อนที่จะแปลงเป็น json เพียงเพิ่มรหัสการแปลงของคุณในโปรแกรมเปลี่ยนไฟล์และคุณก็พร้อมที่จะไป
Claudiu

11

ดูเหมือนสิ่งที่คุณถามเกี่ยวกับการทำให้โครงสร้างข้อมูลของอินสแตนซ์ของ Django เป็นอนุกรมสำหรับการทำงานร่วมกัน ผู้โพสต์อื่น ๆ นั้นถูกต้อง: หากคุณต้องการให้แบบฟอร์มต่อเนื่องนั้นใช้กับแอพพลิเคชั่นหลามที่สามารถสืบค้นฐานข้อมูลผ่าน 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?"}}

แต่นั่นจะตัดส่วนที่เป็นสี่เหลี่ยมจัตุรัสทั้งหมดออกไม่ใช่เฉพาะด้านนอก ดีขึ้นหรือไม่ "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]]))

นี่คือ verbose ที่เทียบเท่ากับสิ่งที่เกลียดชังผู้หนึ่งตอร์ปิโด:

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
แม้ว่าแนวคิดนี้อาจดูดีในตอนแรก แต่ก็ควรชี้ให้เห็นว่ามีผลที่ตามมาจากการใช้วิธีนี้ คุณกำลังผูกเอาต์พุตซีเรียลไลซ์เซชั่นหนึ่งที่เฉพาะเจาะจงกับโมเดลของคุณ
Jonas Geiregat

@JonasGeiregat เนื่องจากวิธีการนี้ถูกกำหนดตามแบบจำลองต่อแบบจำลองมีอะไรผิดปกติกับวิธีการหรือไม่ น่าเศร้าที่ดูเหมือนว่าจะเป็นวิธีเดียวที่จะส่งคืนวัตถุ json ที่มีทั้งฟิลด์และคีย์หลักของอินสแตนซ์
dopatraman

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 (ของวัตถุ) ไม่ใช่วัตถุเปล่าซึ่งเป็นสิ่งที่ OP ถาม iow นี้ไม่แตกต่างจากวิธีการทำให้เป็นอันดับปกติ
Julian

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

ฉันได้รับ 'วัตถุตัวสร้างไม่มีแอตทริบิวต์' ถัดไป ' ความคิดใด ๆ
2880391

1
@ user2880391 ฉันได้อัปเดตสิ่งนี้สำหรับ Python 3 แล้วมันแก้ไขได้หรือไม่
Zags

4

.values() คือสิ่งที่ฉันต้องการในการแปลงอินสแตนซ์โมเดลเป็น JSON

.values ​​() เอกสารประกอบ: https://docs.djangoproject.com/en/3.0/ref/models/querysets/#values

ตัวอย่างการใช้งานที่มีรูปแบบที่เรียกว่าโครงการ

หมายเหตุ: ฉันใช้ 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รุ่นของ 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
Julio Marins

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 โดยตรงเคารพฟิลด์ที่คุณกำหนดไว้เช่นเดียวกับการเชื่อมโยงใด ๆ ฯลฯ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.