在 Django Rest Framework 的响应中包含中介(通过模型)
- 2025-03-18 08:55:00
- admin 原创
- 46
问题描述:
我有一个关于处理 m2m / through 模型及其在 django rest 框架中的呈现的问题。让我们举一个经典的例子:
模型.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()
序列化器.py:
imports...
class MemberSerializer(ModelSerializer):
class Meta:
model = Member
class GroupSerializer(ModelSerializer):
class Meta:
model = Group
视图.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-tastypie 更直接,但遇到了一些其他问题,我更喜欢 rest-framework。
解决方案 1:
怎么样......
在你的 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', )
其总体效果是创建一个序列化值,即组,其来源是您想要的成员资格,然后它使用自定义序列化器提取您想要显示的位。
编辑:正如@bryanph所评论的,在DRF 3.0中serializers.field
被重命名为serializers.ReadOnlyField
,因此应该是:
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:
我遇到了这个问题,我的解决方案(使用 DRF 3.6)是在对象上使用 SerializerMethodField 并像这样明确查询成员资格表:
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]
这将返回 groups 键的字典列表,其中每个字典都是从 MembershipSerializer 序列化的。为了使其可写,您可以在 MemberSerializer 中定义自己的创建/更新方法,在其中迭代输入数据并明确创建或更新 Membership 模型实例。
解决方案 3:
我刚刚遇到了同样的问题,最后我用组查询集上的注释解决了它。
from django.db.models import F
class MemberSerializer(ModelSerializer):
groups = serializers.SerializerMethodField()
class Meta:
model = Member
def get_groups(self, instance):
groups = instance.groups.all().annotate(join_date=F(membership__join_date))
return GroupSerializer(groups, many=True).data
class GroupSerializer(ModelSerializer):
join_date = serializers.CharField(required=False) # so the serializer still works without annotation
class Meta:
model = Group
fields = ..., 'join_date']
解决方案 4:
注意:作为一名软件工程师,我喜欢使用架构,并且我对分层开发方法进行了深入研究,因此我将根据层级来回答这个问题。
据我了解,这是解决方案 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()
序列化器.py
import serializer
class AllSerializer(serializer.Serializer):
group_id = serializer.IntegerField()
group_name = serializer.CharField(max_length = 20)
join_date = serializer.DateTimeField()
自定义模型.py
imports...
class AllDataModel():
group_id = ""
group_name = ""
join_date = ""
业务逻辑.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,它将返回从数据访问层过滤的对象,但由于我必须以快速的方式回答问题,所以我调整了业务逻辑层中的代码!
扫码咨询,免费领取项目管理大礼包!