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)
说明
SlugRelatedField会自动根据传入的slug查询对应Role对象,查询不到时会返回自定义的错误信息,完全匹配需求。- 替换
WritableNestedModelSerializer为普通ModelSerializer,去掉不必要的嵌套创建功能。 - 将密码字段设为非必填,避免每次更新用户都要传入密码。
测试验证
- 传入
{"role":{"slug":"admin"}}:成功关联id为1的Role记录,返回更新后的用户数据。 - 传入
{"role":{"slug":"foo"}}:返回错误信息{"role":["foo不存在于Role表"]}。
内容的提问来源于stack exchange,提问作者i'm fat.
相关产品推荐
相关产品推荐

