web-dev-qa-db-fra.com

Inclure l'intermédiaire (via le modèle) dans les réponses dans Django Rest Framework

J'ai une question sur le traitement des modèles m2m/through et leur présentation dans le cadre de repos Django rest. Prenons un exemple classique:

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

Lorsque j'obtiens une instance de membre, je reçois avec succès tous les champs du membre et également ses groupes - cependant, je ne reçois que les détails des groupes, sans détails supplémentaires provenant du modèle d'adhésion.

En d'autres termes, j'attends de recevoir:

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

Notez la join_date .

J'ai essayé tant de solutions, y compris bien sûr page officielle de Django Rest-Framework à ce sujet et personne ne semble donner une réponse claire à ce sujet - que dois-je faire pour inclure ces champs supplémentaires ? Je l'ai trouvé plus simple avec Django-tastypie mais j'ai eu d'autres problèmes et je préfère rest-framework.

94
mllm

Que diriez-vous.....

Sur votre MemberSerializer, définissez un champ comme celui-ci:

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

puis sur votre sérialiseur d'adhésion, vous pouvez créer ceci:

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

Cela a pour effet global de créer une valeur sérialisée, des groupes, qui a comme source l'appartenance que vous souhaitez, puis il utilise un sérialiseur personnalisé pour extraire les bits que vous souhaitez afficher.

EDIT: comme commenté par @bryanph, serializers.field a été renommé serializers.ReadOnlyField dans DRF 3.0, ceci devrait donc se lire:

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

pour toutes les implémentations modernes

122
thebaron

J'étais confronté à ce problème et ma solution (en utilisant DRF 3.6) était d'utiliser SerializerMethodField sur l'objet et d'interroger explicitement la table Membership comme ceci:

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]

Cela renverra une liste de dictés pour la clé de groupes où chaque dict est sérialisé à partir du MembershipSerializer. Pour le rendre accessible en écriture, vous pouvez définir votre propre méthode de création/mise à jour dans MemberSerializer où vous parcourez les données d'entrée et créez ou mettez à jour explicitement des instances de modèle d'appartenance.

13
FariaC