You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 14:15:30