如何用Django REST Framework在不同视图中灵活控制字段显示?
在Django REST Framework里灵活控制字段显示与外键序列化
问题说明
用DRF开发时,怎么不用写重复代码,就能让同一个模型的字段在不同视图里按需显示/隐藏?比如现在有这两个模型:
class Realtor(models.Model): name = models.CharField(max_length=100) class House(models.Model): name = models.CharField(max_length=100) address = models.TextField() price = models.DecimalField(max_digits=10, decimal_places=2) realtor = models.ForeignKey(Realtor, related_name='houses', on_delete=models.CASCADE)
要实现两个需求:
- 一个视图只显示House的
name字段,另一个视图显示所有字段; - 查询House时,显示关联Realtor的
name,而不是默认的ID。
一、按需控制House字段的显示
不用写多个重复的Serializer,有这两种简洁的办法:
1. 基础Serializer+继承扩展
先写一个包含所有字段的基础Serializer,其他Serializer继承它再调整字段:
from rest_framework import serializers # 基础Serializer,包含House所有字段 class BaseHouseSerializer(serializers.ModelSerializer): class Meta: model = House fields = '__all__' # 只显示name的精简版Serializer,继承基础类指定字段 class MinimalHouseSerializer(BaseHouseSerializer): class Meta(BaseHouseSerializer.Meta): fields = ('name',) # 如果需要保留大部分字段,用exclude更方便(比如排除address、price、realtor) # class MinimalHouseSerializer(BaseHouseSerializer.Meta): # exclude = ('address', 'price', 'realtor')
然后在视图里分别用这两个Serializer就行:
from rest_framework import viewsets # 只显示name的视图 class MinimalHouseViewSet(viewsets.ReadOnlyModelViewSet): queryset = House.objects.all() serializer_class = MinimalHouseSerializer # 显示所有字段的视图 class FullHouseViewSet(viewsets.ReadOnlyModelViewSet): queryset = House.objects.all() serializer_class = BaseHouseSerializer
2. 动态控制Serializer字段
如果想在同一个视图里根据场景切换(比如列表页显示精简,详情页显示完整),可以给Serializer加动态逻辑:
class DynamicHouseSerializer(serializers.ModelSerializer): def __init__(self, *args, **kwargs): # 取出传入的exclude_fields参数,默认空列表 exclude_fields = kwargs.pop('exclude_fields', []) super().__init__(*args, **kwargs) # 删除不需要的字段 for field in exclude_fields: if field in self.fields: del self.fields[field] class Meta: model = House fields = '__all__'
然后在视图里重写get_serializer方法传参数:
class HouseViewSet(viewsets.ReadOnlyModelViewSet): queryset = House.objects.all() def get_serializer(self, *args, **kwargs): # 列表页显示精简字段,详情页显示全部 if self.action == 'list': kwargs['exclude_fields'] = ['address', 'price', 'realtor'] return super().get_serializer(*args, **kwargs)
二、显示Realtor的name而非ID
要把外键的ID换成关联对象的name,常用三种方式:
1. 用StringRelatedField
如果你的Realtor模型的__str__方法返回的是name(比如def __str__(self): return self.name),直接用这个字段就行:
class BaseHouseSerializer(serializers.ModelSerializer): realtor = serializers.StringRelatedField() class Meta: model = House fields = '__all__'
注意:这个字段是只读的,要是需要支持创建/更新House时传入数据,得换其他方法。
2. 嵌套Realtor Serializer
如果以后要给Realtor加更多显示字段,这种方式更灵活:
# 先写Realtor的Serializer,只返回name class RealtorSerializer(serializers.ModelSerializer): class Meta: model = Realtor fields = ('name',) # 在House的Serializer里嵌套使用 class BaseHouseSerializer(serializers.ModelSerializer): realtor = RealtorSerializer() class Meta: model = House fields = '__all__'
如果需要支持写入(比如创建House时关联Realtor),得重写Serializer的create或update方法来处理嵌套逻辑。
3. 用SlugRelatedField
要是想在创建/更新House时,直接传入Realtor的name来关联(而不是ID),用这个字段最合适:
class BaseHouseSerializer(serializers.ModelSerializer): realtor = serializers.SlugRelatedField( slug_field='name', # 指定用Realtor的name字段 queryset=Realtor.objects.all() ) class Meta: model = House fields = '__all__'
这个字段既能显示Realtor的name,也支持写入时通过name关联对象。
内容的提问来源于stack exchange,提问作者matthias3132
相关产品推荐
相关产品推荐

