Sertakan perantara (melalui model) dalam tanggapan dalam Kerangka Rest Django

110

Saya punya pertanyaan tentang berurusan dengan m2m / through model dan presentasi mereka dalam kerangka kerja django. Mari kita ambil contoh klasik:

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

Saat MENDAPATKAN instance Anggota, saya berhasil menerima semua bidang anggota dan juga grupnya - namun saya hanya mendapatkan detail grup, tanpa detail tambahan yang berasal dari model Keanggotaan.

Dengan kata lain saya berharap untuk menerima:

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

Catat join_date .

Saya telah mencoba oh begitu banyak solusi, termasuk tentu saja halaman resmi Django Rest-Framework tentang hal itu dan tampaknya tidak ada yang memberikan jawaban jelas yang tepat tentangnya - apa yang perlu saya lakukan untuk menyertakan bidang tambahan ini? Saya merasa lebih mudah dengan django-tastypie tetapi memiliki beberapa masalah lain dan lebih memilih rest-framework.

mllm
sumber
Akankah eugene-yeo.me/2012/12/4/… membantu?
karthikr
8
Ini untuk pai yang enak, saya sedang bekerja dengan Django Rest Framework.
mllm

Jawaban:

139

Bagaimana tentang.....

Di MemberSerializer Anda, tentukan bidang di atasnya seperti:

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

dan kemudian pada serializer keanggotaan Anda, Anda dapat membuat ini:

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

Itu memiliki efek keseluruhan untuk membuat nilai serial, grup, yang memiliki sumber keanggotaan yang Anda inginkan, dan kemudian menggunakan serializer kustom untuk menarik bit yang ingin Anda tampilkan.

EDIT: seperti yang dikomentari oleh @bryanph, serializers.fielddiubah namanya menjadi serializers.ReadOnlyFielddi DRF 3.0, jadi ini akan membaca:

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

untuk implementasi modern apa pun

thebaron
sumber
2
fyi, saya sudah mencoba banyak varian ini dan saya tidak bisa membuatnya berfungsi. Ini tidak ada di dokumen resmi? Di mana membership_set ditentukan?
tanah liat
3
membership_setadalah nama terkait default untuk Anggota -> Keanggotaan
dustinfarris
Bagian yang sulit bagi saya adalah menemukan nama "membership_set". Saya memiliki model melalui tanpa nama "terkait" yang eksplisit, jadi saya harus menebak namanya, dengan membaca dokumen di Django Many to Many .
miceno
ini berfungsi dengan baik, terima kasih atas petunjuknya. Saya pikir bagaimanapun DRF dalam kasus ini agak berlawanan dengan intuisi karena Anggota kelas sudah mendefinisikan bidang m2m yang disebut grup dan solusi ini tampaknya menimpa bidang di penyerial dengan memaksanya untuk menunjuk ke hubungan terbalik dari model melalui. Saya tidak terlalu memahami detail implementasi DRF, tetapi mungkin dengan introspeksi model, itu bisa diserahkan secara otomatis. hanya beberapa makanan untuk dipikirkan :)
gru
Apakah Anda dapat memperbarui kami tentang apakah ini berfungsi dengan DRF versi terbaru? Atau setidaknya beri tahu versi mana yang Anda gunakan? Saya tidak dapat membuat DRF untuk mengembalikan model melalui lapangan - itu selalu berakhir dengan relasi asli (bukan Keanggotaan - itu akan selalu mengembalikan Grup).
Andrey Cizov
18

Saya menghadapi masalah ini dan solusi saya (menggunakan DRF 3.6) adalah menggunakan SerializerMethodField pada objek dan secara eksplisit menanyakan tabel Keanggotaan seperti:

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]

Ini akan mengembalikan daftar dicts untuk kunci grup di mana setiap dict diserialkan dari MembershipSerializer. Untuk membuatnya dapat ditulisi, Anda dapat menentukan metode buat / perbarui Anda sendiri di dalam MemberSerializer tempat Anda mengulang data masukan dan secara eksplisit membuat atau memperbarui contoh model Keanggotaan.

FariaC
sumber
-4

CATATAN: Sebagai Insinyur Perangkat Lunak, saya suka menggunakan Arsitektur dan saya telah bekerja keras pada Pendekatan Berlapis untuk Pengembangan jadi saya akan Menjawabnya dengan Menghormati Tingkatan.

Saat saya memahami Masalahnya, Inilah Solution 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

Secara teknis, Anda harus meneruskan Request ke DataAccessLayer yang akan mengembalikan Objek Tersaring dari Lapisan Akses Data tetapi karena saya harus Menjawab Pertanyaan dengan Cepat, jadi saya menyesuaikan Kode di Lapisan Logika Bisnis!

Syed Faizan
sumber
1
Ini adalah Pendekatan Kustomisasi Penuh yang saya gunakan untuk sebagian besar pengembangan Rest API saya karena saya sebenarnya bukan penggemar pekerjaan dengan Bounds walaupun Django Rest Framework cukup fleksibel!
Syed Faizan
2
Ini waaaay terlalu direkayasa, juga tidak menggunakan DRF.
michauwilliam