如何用DRF嵌套多对多序列化器实现职位与上级模型关联操作
问题描述
现有两个Django模型:
class PositionModel(BaseModel): """ User Position/Designation Model """ name = models.CharField(max_length=255) class Meta: ordering = ["created_at"] def __str__(self): return f"{self.name}" class SuperiorModel(BaseModel): """ Super model for position """ position = models.OneToOneField(PositionModel, on_delete=models.CASCADE, related_name='position_superior') superior = models.ManyToManyField(PositionModel, blank=True) def __str__(self): return f'{self.position.name}'
同时配置了如下信号:
@receiver(post_save, sender=PositionModel) def create_position_instances(sender, instance, created, **kwargs): if created: SuperiorModel.objects.create( position=instance ) @receiver(post_save, sender=PositionModel) def save_position_instances(sender, instance, **kwargs): instance.position_superior.save()
需要实现两个需求:
- 创建
PositionModel实例时,同步将请求中的superior字段(ID数组,可为空)保存到关联的SuperiorModel中 - GET请求返回的数据格式与POST载荷一致:
{ "name": "test position", "superior": [1,2] // 无上级时为空数组 }
实现方案
1. 编写适配的序列化器
核心是通过序列化器把superior字段和SuperiorModel的多对多关系做映射,处理读写逻辑:
from rest_framework import serializers from .models import PositionModel, SuperiorModel class PositionSerializer(serializers.ModelSerializer): # 定义superior为可多选的ID字段,支持空数组 superior = serializers.PrimaryKeyRelatedField( queryset=PositionModel.objects.all(), many=True, required=False, allow_empty=True ) class Meta: model = PositionModel fields = ['id', 'name', 'superior'] def create(self, validated_data): # 先把superior数据提出来,默认空列表 superior_list = validated_data.pop('superior', []) # 创建职位实例 position = PositionModel.objects.create(**validated_data) # 拿信号自动创建的SuperiorModel实例 superior_rel = position.position_superior # 设置上级关联 superior_rel.superior.set(superior_list) return position def to_representation(self, instance): # 重写返回格式,把关联的上级转成ID数组 ret = super().to_representation(instance) ret['superior'] = list(instance.position_superior.superior.values_list('id', flat=True)) return ret
2. 优化信号逻辑
原来的save_position_instances信号没有实际作用,直接删除;将创建信号改为get_or_create,避免手动创建职位时触发重复创建的异常:
from django.db.models.signals import post_save from django.dispatch import receiver from .models import PositionModel, SuperiorModel @receiver(post_save, sender=PositionModel) def create_position_instances(sender, instance, created, **kwargs): if created: SuperiorModel.objects.get_or_create(position=instance)
3. 配置视图和路由
使用DRF的ModelViewSet快速实现完整的CRUD接口:
# views.py from rest_framework import viewsets from .models import PositionModel from .serializers import PositionSerializer class PositionViewSet(viewsets.ModelViewSet): queryset = PositionModel.objects.all() serializer_class = PositionSerializer
# urls.py from django.urls import path, include from rest_framework.routers import DefaultRouter from .views import PositionViewSet router = DefaultRouter() router.register(r'positions', PositionViewSet) urlpatterns = [ path('', include(router.urls)), ]
完成以上配置后:
- POST请求提交指定格式的JSON时,会自动创建
PositionModel实例,并将superior字段对应的职位关联到SuperiorModel中 - GET请求返回的数据结构与POST请求体完全一致,无上级职位时
superior字段返回空数组
内容的提问来源于stack exchange,提问作者Nahidujjaman Hridoy
相关产品推荐
相关产品推荐

