Schließen Sie einen Vermittler (durch Modell) in die Antworten des Django Rest Framework ein


110

Ich habe eine Frage zum Umgang mit m2m / through-Modellen und deren Darstellung im Django Rest Framework. Nehmen wir ein klassisches Beispiel:

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

Wenn ich eine Instanz eines Mitglieds erhalte, erhalte ich erfolgreich alle Felder des Mitglieds und auch seine Gruppen. Ich erhalte jedoch nur die Gruppendetails, ohne zusätzliche Details, die aus dem Mitgliedschaftsmodell stammen.

Mit anderen Worten, ich erwarte Folgendes:

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

Beachten Sie das join_date .

Ich habe so viele Lösungen ausprobiert, einschließlich natürlich der offiziellen Seite des Django Rest-Framework, und niemand scheint eine klare Antwort darauf zu geben - was muss ich tun, um diese zusätzlichen Felder einzuschließen? Ich fand es einfacher mit Django-Tastypie, hatte aber einige andere Probleme und bevorzuge Rest-Framework.



8
Dies ist für leckeren Kuchen, ich arbeite mit Django Rest Framework.
Mllm

Antworten:


139

Wie wäre es mit.....

Definieren Sie in Ihrem MemberSerializer ein Feld wie folgt:

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

und dann können Sie auf Ihrem Mitgliedschafts-Serializer Folgendes erstellen:

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

Dies hat den Gesamteffekt, dass ein serialisierter Wert, Gruppen, erstellt wird, dessen Quelle die gewünschte Mitgliedschaft ist, und anschließend ein benutzerdefinierter Serializer verwendet wird, um die anzuzeigenden Bits herauszuholen.

BEARBEITEN: Wie von @bryanph kommentiert, serializers.fieldwurde serializers.ReadOnlyFieldin DRF 3.0 umbenannt, daher sollte dies lauten:

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

für alle modernen Implementierungen


2
Zu Ihrer Information, ich habe viele Varianten davon ausprobiert und ich kann das nicht zum Laufen bringen. Dies ist nicht in den offiziellen Dokumenten? Wo ist Membership_set definiert?
Ton

3
membership_setist der Standardname für Mitglied -> Mitgliedschaft
Dustinfarris

Der Trick für mich war, den Namen "membership_set" zu entdecken. Ich hatte ein Durchgangsmodell ohne expliziten "verwandten" Namen, daher musste ich den Namen erraten, indem ich die Dokumente bei Django Many to Many las .
Miceno

Das funktioniert super, danke für den Hinweis. Ich denke jedoch, dass DRF in diesem Fall etwas eingängig ist, da das Klassenmitglied bereits ein m2m-Feld definiert, das als Gruppen bezeichnet wird, und diese Lösung das Feld im Serialisierer zu überschreiben scheint, indem es gezwungen wird, auf die umgekehrte Beziehung vom Durchgangsmodell zu verweisen. Ich bin nicht sehr mit Details der DRF-Implementierung beschäftigt, aber wahrscheinlich könnte sie bei der Selbstbeobachtung des Modells automatisch übergeben werden. nur ein paar Denkanstöße :)
gru

Könnten Sie uns auf jeden Fall mitteilen, ob dies mit der neuesten Version von DRF funktioniert? Oder zumindest sagen, welche Version Sie verwendet haben? Ich kann DRF nicht dazu bringen, das Durchgangsfeldmodell zurückzugeben - es endet immer mit der ursprünglichen Beziehung (anstelle der Mitgliedschaft würde es immer die Gruppe zurückgeben).
Andrey Cizov

18

Ich war mit diesem Problem konfrontiert und meine Lösung (unter Verwendung von DRF 3.6) bestand darin, SerializerMethodField für das Objekt zu verwenden und die Mitgliedschaftstabelle explizit wie folgt abzufragen:

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]

Dies gibt eine Liste von Diktaten für den Gruppenschlüssel zurück, in dem jedes Diktat vom MembershipSerializer serialisiert wird. Um es beschreibbar zu machen, können Sie im MemberSerializer Ihre eigene Erstellungs- / Aktualisierungsmethode definieren, bei der Sie die Eingabedaten durchlaufen und explizit Mitgliedschaftsmodellinstanzen erstellen oder aktualisieren.


-4

HINWEIS: Als Software-Ingenieur verwende ich gerne Architekturen und habe intensiv an einem mehrschichtigen Ansatz für die Entwicklung gearbeitet, daher werde ich ihn in Bezug auf Ebenen beantworten.

Wie ich das Problem verstanden habe, ist hier die Lösungsmodelle.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

Sie müssten die Anforderung technisch an DataAccessLayer übergeben, der die gefilterten Objekte aus der Datenzugriffsschicht zurückgibt, aber da ich die Frage schnell beantworten muss, habe ich den Code in der Geschäftslogikebene angepasst!


1
Dies ist ein vollständig angepasster Ansatz, den ich für die meisten meiner Rest-API-Entwicklungen verwende, da ich nicht wirklich ein Fan der Arbeit mit Bounds bin, obwohl das Django Rest Framework sehr flexibel ist!
Syed Faizan

2
Dies ist waaaay zu überentwickelt, auch es verwendet nicht einmal DRF.
Michauwilliam
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.