รวมตัวกลาง (ผ่านโมเดล) ในการตอบสนองใน Django Rest Framework


111

ฉันมีคำถามเกี่ยวกับการจัดการกับ m2m / through models และการนำเสนอใน django rest framework ลองมาดูตัวอย่างคลาสสิก:

Models.py:

from django.db import models

class Member(models.Model):
    name = models.CharField(max_length = 20)
    groups = models.ManyToManyField('Group', through = 'Membership')

class Group(models.Model):
    name = models.CharField(max_length = 20)

class Membership(models.Model):
    member = models.ForeignKey('Member')
    group = models.ForeignKey('Group')
    join_date = models.DateTimeField()

serializers.py:

imports...

class MemberSerializer(ModelSerializer):
    class Meta:
        model = Member

class GroupSerializer(ModelSerializer):
    class Meta:
        model = Group

views.py:

imports...

class MemberViewSet(ModelViewSet):
    queryset = Member.objects.all()
    serializer_class = MemberSerializer

class GroupViewSet(ModelViewSet):
    queryset = Group.objects.all()
    serializer_class = GroupSerializer

เมื่อได้รับอินสแตนซ์ของสมาชิกฉันจะได้รับฟิลด์ทั้งหมดของสมาชิกและกลุ่มของมันสำเร็จ - อย่างไรก็ตามฉันได้รับรายละเอียดของกลุ่มเท่านั้นโดยไม่มีรายละเอียดเพิ่มเติมที่มาจากรูปแบบการเป็นสมาชิก

กล่าวอีกนัยหนึ่งฉันคาดว่าจะได้รับ:

{
   'id' : 2,
   'name' : 'some member',
   'groups' : [
      {
         'id' : 55,
         'name' : 'group 1'
         'join_date' : 34151564
      },
      {
         'id' : 56,
         'name' : 'group 2'
         'join_date' : 11200299
      }
   ]
}

หมายเหตุjoin_date

ฉันได้ลองใช้วิธีแก้ปัญหามากมายรวมถึงหน้าอย่างเป็นทางการของDjango Rest-Framework เกี่ยวกับเรื่องนี้และดูเหมือนจะไม่มีใครให้คำตอบที่ชัดเจนเกี่ยวกับเรื่องนี้ - ฉันต้องทำอย่างไรเพื่อรวมฟิลด์พิเศษเหล่านี้ ฉันพบว่ามันตรงไปตรงมามากกว่ากับ django-tasteypie แต่มีปัญหาอื่น ๆ และชอบที่จะวางกรอบ


จะeugene-yeo.me/2012/12/4/...ความช่วยเหลือ?
karthikr

8
สำหรับพายแสนอร่อยฉันกำลังทำงานร่วมกับ Django Rest Framework
mllm

คำตอบ:


141

เกี่ยวกับ.....

ใน MemberSerializer ของคุณให้กำหนดฟิลด์ดังต่อไปนี้:

groups = MembershipSerializer(source='membership_set', many=True)

จากนั้นในซีเรียลไลเซอร์สมาชิกของคุณคุณสามารถสร้างสิ่งนี้:

class MembershipSerializer(serializers.HyperlinkedModelSerializer):

    id = serializers.Field(source='group.id')
    name = serializers.Field(source='group.name')

    class Meta:
        model = Membership

        fields = ('id', 'name', 'join_date', )

ซึ่งมีผลโดยรวมในการสร้างค่าที่เป็นอนุกรมกลุ่มที่มีแหล่งที่มาของการเป็นสมาชิกที่คุณต้องการจากนั้นใช้ serializer ที่กำหนดเองเพื่อดึงบิตที่คุณต้องการแสดงออกมา

แก้ไข: ตามความคิดเห็นของ @bryanph serializers.fieldถูกเปลี่ยนชื่อเป็นserializers.ReadOnlyFieldDRF 3.0 ดังนั้นสิ่งนี้ควรอ่าน:

class MembershipSerializer(serializers.HyperlinkedModelSerializer):

    id = serializers.ReadOnlyField(source='group.id')
    name = serializers.ReadOnlyField(source='group.name')

    class Meta:
        model = Membership

        fields = ('id', 'name', 'join_date', )

สำหรับการใช้งานที่ทันสมัย


2
fyi ฉันลองใช้หลายรูปแบบแล้ว แต่ไม่สามารถใช้งานได้ สิ่งนี้ไม่อยู่ในเอกสารอย่างเป็นทางการ? Members_set กำหนดไว้ที่ไหน
ดิน

3
membership_setเป็นชื่อที่เกี่ยวข้องเริ่มต้นสำหรับ Member -> Membership
dustinfarris

ส่วนเคล็ดลับสำหรับฉันคือการค้นพบชื่อ "Members_set" ผมมีผ่านรูปแบบที่ไม่มีอย่างชัดเจน "ที่เกี่ยวข้องกับ" ชื่อเพื่อให้ฉันได้ที่จะคาดเดาชื่อของมันโดยการอ่านเอกสารที่Django หลายหลาย
miceno

มันใช้งานได้ดีขอบคุณสำหรับคำใบ้ ฉันคิดว่า DRF ในกรณีนี้ค่อนข้างขัดแย้งกันเนื่องจาก class Member กำหนดฟิลด์ m2m ที่เรียกว่า groups ไว้แล้วและโซลูชันนี้ดูเหมือนจะแทนที่ฟิลด์ใน serialiser โดยบังคับให้ชี้ไปที่ความสัมพันธ์ย้อนกลับจากแบบจำลองผ่าน ฉันไม่ค่อยเข้าใจรายละเอียดการใช้งาน DRF มากนัก แต่อาจเป็นไปได้ด้วยการวิปัสสนาแบบจำลองที่สามารถส่งได้โดยอัตโนมัติ แค่อาหารสำหรับความคิด :)
gru

มีกรณีใดบ้างที่คุณสามารถอัปเดตให้เราทราบว่าสามารถใช้งานกับ DRF เวอร์ชันล่าสุดได้หรือไม่ หรืออย่างน้อยก็บอกรุ่นที่คุณใช้? ฉันไม่สามารถสร้าง DRF เพื่อส่งคืนโมเดลฟิลด์ผ่านได้ - มันมักจะลงท้ายด้วยความสัมพันธ์ดั้งเดิม (แทนที่จะเป็นสมาชิก - มันจะส่งคืนกลุ่มเสมอ)
Andrey Cizov

18

ฉันประสบปัญหานี้และวิธีแก้ปัญหาของฉัน (โดยใช้ DRF 3.6) คือใช้ SerializerMethodField กับวัตถุและสอบถามตาราง Membership อย่างชัดเจนดังนี้:

class MembershipSerializer(serializers.ModelSerializer):
    """Used as a nested serializer by MemberSerializer"""
    class Meta:
        model = Membership
        fields = ('id','group','join_date')

class MemberSerializer(serializers.ModelSerializer):
    groups = serializers.SerializerMethodField()

    class Meta:
        model = Member
        fields = ('id','name','groups')

    def get_groups(self, obj):
        "obj is a Member instance. Returns list of dicts"""
        qset = Membership.objects.filter(member=obj)
        return [MembershipSerializer(m).data for m in qset]

สิ่งนี้จะส่งคืนรายการของคำสั่งสำหรับคีย์กลุ่มที่แต่ละคำสั่งต่ออนุกรมจาก MembershipSerializer เพื่อให้สามารถเขียนได้คุณสามารถกำหนดวิธีการสร้าง / อัปเดตของคุณเองภายใน MemberSerializer ซึ่งคุณจะวนซ้ำข้อมูลอินพุตและสร้างหรืออัปเดตอินสแตนซ์โมเดลสมาชิกอย่างชัดเจน


-4

หมายเหตุ: ในฐานะวิศวกรซอฟต์แวร์ฉันชอบใช้งานสถาปัตยกรรมและฉันได้ทำงานอย่างลึกซึ้งเกี่ยวกับ Layered Approach for Development ดังนั้นฉันจะตอบด้วยความเคารพต่อ Tiers

ตามที่ฉันเข้าใจปัญหานี่คือโซลูชัน models.py

class Member(models.Model):
    member_id = models.AutoField(primary_key=True)
    member_name = models.CharField(max_length = 

class Group(models.Model):
    group_id = models.AutoField(primary_key=True)
    group_name = models.CharField(max_length = 20)
    fk_member_id = models.ForeignKey('Member', models.DO_NOTHING, 
                             db_column='fk_member_id', blank=True, null=True)

class Membership(models.Model):
    membershipid = models.AutoField(primary_key=True)
    fk_group_id = models.ForeignKey('Group', models.DO_NOTHING, 
                             db_column='fk_member_id', blank=True, null=True)
    join_date = models.DateTimeField()

serializers.py

import serializer

class AllSerializer(serializer.Serializer):
    group_id = serializer.IntegerField()
    group_name = serializer.CharField(max_length = 20)
    join_date = serializer.DateTimeField()

CustomModels.py

imports...

    class AllDataModel():
        group_id = ""
        group_name = ""
        join_date = ""

BusinessLogic.py

imports ....
class getdata(memberid):
    alldataDict = {}
    dto = []
    Member = models.Members.objects.get(member_id=memberid) #or use filter for Name
    alldataDict["MemberId"] = Member.member_id
    alldataDict["MemberName"] = Member.member_name
    Groups = models.Group.objects.filter(fk_member_id=Member)
    for item in Groups:
        Custommodel = CustomModels.AllDataModel()
        Custommodel.group_id = item.group_id
        Custommodel.group_name = item.group_name
        Membership = models.Membership.objects.get(fk_group_id=item.group_id)
        Custommodel.join_date = Membership.join_date
        dto.append(Custommodel)
    serializer = AllSerializer(dto,many=True)
    alldataDict.update(serializer.data)
    return alldataDict

ในทางเทคนิคคุณจะต้องส่งคำขอไปยัง DataAccessLayer ซึ่งจะส่งคืนวัตถุที่กรองจาก Data Access Layer แต่เนื่องจากฉันต้องตอบคำถามในลักษณะที่รวดเร็วดังนั้นฉันจึงปรับรหัสใน Business Logic Layer!


1
นี่เป็นแนวทางที่กำหนดเองเต็มรูปแบบซึ่งฉันใช้สำหรับการพัฒนา Rest API ส่วนใหญ่เนื่องจากฉันไม่ได้เป็นแฟนตัวยงของการทำงานกับ Bounds แม้ว่า Django Rest Framework จะค่อนข้างยืดหยุ่นก็ตาม!
Syed Faizan

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