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

如何在DRF中更新多对多字段 实现用户学校关联增删操作

实现方案

Django的ManyToMany字段自带add()和remove()方法,可以直接处理关联关系的增删,针对你的需求完整实现步骤如下:

1. 修正序列化器

你之前写的序列化器存在几个基础问题:没有继承DRF的序列化器基类、字段配置参数写错(field应为fields,单元素元组缺少尾部逗号),而且缺少必要的入参和校验逻辑。因为不需要直接创建/修改User或School实例,仅操作两者的关联关系,用普通Serializer更灵活:

from rest_framework import serializers
from django.contrib.auth import get_user_model
from .models import School

User = get_user_model()

class SchoolMembershipOperationSerializer(serializers.Serializer):
    email = serializers.EmailField(required=True, help_text="用户邮箱")
    school_id = serializers.IntegerField(required=True, help_text="学校ID")
    action = serializers.ChoiceField(
        choices=[("add", "添加关联"), ("remove", "移除关联")],
        required=True,
        help_text="操作类型"
    )

    def validate(self, attrs):
        # 校验用户是否存在
        try:
            user = User.objects.get(email=attrs["email"])
        except User.DoesNotExist:
            raise serializers.ValidationError({"email": "对应邮箱的用户不存在"})
        # 校验学校是否存在
        try:
            school = School.objects.get(id=attrs["school_id"])
        except School.DoesNotExist:
            raise serializers.ValidationError({"school_id": "对应ID的学校不存在"})
        
        # 把查到的实例存入校验后的数据,视图里直接调用,避免重复查库
        attrs["user"] = user
        attrs["school"] = school
        return attrs

2. 编写视图逻辑

用DRF的基础APIView实现即可,核心逻辑是根据action参数调用M2M字段的add/remove方法,提前做好重复操作的判断:

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.permissions import IsAuthenticated
from .serializers import SchoolMembershipOperationSerializer

class SchoolMembershipManageView(APIView):
    # 开启登录校验,可按需调整权限规则
    permission_classes = [IsAuthenticated]

    def post(self, request):
        serializer = SchoolMembershipOperationSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        
        user = serializer.validated_data["user"]
        school = serializer.validated_data["school"]
        action = serializer.validated_data["action"]

        # 细粒度权限校验:仅允许学校校长或平台管理员操作成员
        if not (request.user.is_staff or school.principal == request.user):
            return Response(
                {"detail": "你没有权限操作该学校的成员列表"},
                status=status.HTTP_403_FORBIDDEN
            )

        if action == "add":
            # 避免重复添加关联
            if user.schools.filter(id=school.id).exists():
                return Response(
                    {"detail": "该用户已关联此学校,无需重复添加"},
                    status=status.HTTP_400_BAD_REQUEST
                )
            user.schools.add(school)
            return Response({"detail": "关联添加成功"}, status=status.HTTP_200_OK)
        
        # remove操作逻辑
        if not user.schools.filter(id=school.id).exists():
            return Response(
                {"detail": "该用户未关联此学校,无法移除"},
                status=status.HTTP_400_BAD_REQUEST
            )
        user.schools.remove(school)
        return Response({"detail": "关联移除成功"}, status=status.HTTP_200_OK)

3. 配置路由

在对应app的urls.py中添加路由映射:

from django.urls import path
from . import views

urlpatterns = [
    # 其他已有路由...
    path(
        "api/school/membership/",
        views.SchoolMembershipManageView.as_view(),
        name="school-membership-manage"
    ),
]

注意事项

  • 你的User模型里的updated_at字段配置有误,当前写的auto_now_add=True只会在用户首次创建时赋值,后续更新用户数据时该字段不会自动更新,建议改成auto_now=True
  • 接口请求方式为POST,请求体需要传三个参数:email(用户邮箱)、school_id(学校ID)、action(值为add或remove)
  • 如果需要批量给用户添加/移除多个学校,可以把入参调整为支持邮箱列表、学校ID列表,逻辑和单条操作一致,循环调用add/remove即可

内容的提问来源于stack exchange,提问作者Random User

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.01 22:04:04