如何在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
相关产品推荐
相关产品推荐

