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

Django REST UpdateAPIView更新异常:需校验关联Role存在性

问题

在使用Django REST Framework的UpdateAPIView更新CustomUser数据时,需要关联数据库中已存在的Role记录;若请求传入的Role slug不存在,需抛出错误提示。当前用WritableNestedModelSerializer实现嵌套更新时,会自动创建不存在的Role,不符合预期。

现有代码如下:

models.py

class CustomUser(AbstractBaseUser, PermissionsMixin):
    uuid = models.UUIDField(default=uuid.uuid4, primary_key=True, editable=False)
    email = models.EmailField(_('email address'), unique=True)
    role = models.OneToOneField('Role', related_name='user', blank=True, null=True, on_delete=models.PROTECT)
    # 省略其他字段...

class Role(models.Model):
    slug = models.SlugField(unique=True)

    def __str__(self):
        return self.slug

serializers.py

from drf_writable_nested import WritableNestedModelSerializer
from rest_framework import serializers

from accounts.models import CustomUser, Role


class RoleSerializer(serializers.ModelSerializer):
    class Meta:
        model = Role
        fields = ('slug',)


class UserSerializer(WritableNestedModelSerializer):
    role = RoleSerializer()

    class Meta:
        model = CustomUser
        fields = ('uuid', 'email', 'role', 'password')
        extra_kwargs = {'password': {'write_only': True, 'required': True}}

    def create(self, validated_data):
        return CustomUser.objects.create_user(**validated_data)

views.py

class UserUpdateView(generics.UpdateAPIView):
    queryset = CustomUser.objects.all()
    serializer_class = UserSerializer

数据库中已存在Role记录:

id:1 | slug:admin

预期效果:

  • PATCH传入{"role":{"slug":"foo"}}时,返回错误:foo不存在于Role表
  • PATCH传入{"role":{"slug":"admin"}}时,更新成功

解决方案

不需要用WritableNestedModelSerializer,直接用DRF自带的SlugRelatedField处理Role关联,它会自动验证slug是否存在,且只会关联已有记录,不会创建新的。

修改serializers.py如下:

from rest_framework import serializers
from accounts.models import CustomUser, Role


class UserSerializer(serializers.ModelSerializer):
    # 用SlugRelatedField替换嵌套的RoleSerializer
    role = serializers.SlugRelatedField(
        queryset=Role.objects.all(),
        slug_field='slug',
        required=False,  # 匹配原模型中role允许为空的设置
        allow_null=True,
        error_messages={
            'does_not_exist': '{slug}不存在于Role表'
        }
    )

    class Meta:
        model = CustomUser
        fields = ('uuid', 'email', 'role', 'password')
        extra_kwargs = {'password': {'write_only': True, 'required': False}}  # 更新时密码非必填

    def create(self, validated_data):
        return CustomUser.objects.create_user(**validated_data)

说明

  1. SlugRelatedField会自动根据传入的slug查询对应Role对象,查询不到时会返回自定义的错误信息,完全匹配需求。
  2. 替换WritableNestedModelSerializer为普通ModelSerializer,去掉不必要的嵌套创建功能。
  3. 将密码字段设为非必填,避免每次更新用户都要传入密码。

测试验证

  • 传入{"role":{"slug":"admin"}}:成功关联id为1的Role记录,返回更新后的用户数据。
  • 传入{"role":{"slug":"foo"}}:返回错误信息{"role":["foo不存在于Role表"]}。

内容的提问来源于stack exchange,提问作者i'm fat.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 11:01:04