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.
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
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.