Django DRF中使用PATCH请求无法将Boolean字段更新为False
问题背景
你定义的Profile模型代码如下:
class Profile(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE) city = models.CharField(max_length=50, blank=True) country = models.CharField(max_length=50, blank=True) bio = models.CharField(max_length=500, blank=True) profile_pic = models.ImageField(upload_to='profile/%Y/%m/%d', default='media/placeholder.png', blank=False, null=False) is_online = models.BooleanField(default=False) is_active = models.BooleanField(default=False)
你需要实现一个仅通过PATCH方法更新is_active字段的API接口,最初编写的视图调整后如下:
class UpdateProfileActive(generics.UpdateAPIView): queryset = User.objects.all() serializer_class = UpdateUserSerializer class UpdateProfileActive(generics.UpdateAPIView): queryset = User.objects.all() serializer_class = UpdateUserSerializer def update(self, request, *args, **kwargs): instance = self.get_object() serializer = self.get_serializer(instance, data='profile.is_active', partial=True) if serializer.is_valid(): serializer.save() return Response({"message": "user is active field has been updated"}) else: return Response({"message": "failed", "details": serializer.errors})
对应序列化器代码:
class UpdateUserSerializer(serializers.ModelSerializer): email = serializers.EmailField(required=False) city = serializers.CharField(source='profile.city', allow_blank=True, required=False) country = serializers.CharField(source='profile.country', allow_blank=True, required=False) profile_pic = Base64ImageField(source='profile.profile_pic', max_length=None, use_url=True, required=False) is_online = serializers.BooleanField(source='profile.is_online', required=False) is_active = serializers.BooleanField(source='profile.is_active', required=False) class Meta: model = User fields = ['username', 'email', 'password', 'first_name', 'last_name', 'city', 'country', 'profile_pic', 'is_online', 'is_active'] extra_kwargs = {'username': {'required': False}, 'email': {'required': False}, 'password': {'required': False}, 'first_name': {'required': False}, 'last_name': {'required': False}, 'city': {'required': False}, 'country': {'required': False}, 'profile_pic': {'required': False}, 'is_online': {'required': False}, 'is_active': {'required': False}, } def update(self, instance, validated_data): profile_data = validated_data.pop('profile', {}) city = profile_data.get('city') country = profile_data.get('country') profile_pic = profile_data.get('profile_pic') is_online = profile_data.get('is_online') is_active = profile_data.get('is_active') instance = super(UpdateUserSerializer, self).update(instance, validated_data) profile = instance.profile if profile_data: if city: profile.city = city if country: profile.country = country if profile_pic: profile.profile_pic = profile_pic if is_online: profile.is_online = is_online if is_active: profile.is_active = is_active profile.save() return instance
当前故障现象:向接口发送请求体{"is_active": false}时,is_active字段仍然保持原值True无法更新,但将字段设为True时可以正常生效。
故障原因
问题出在两处代码逻辑错误:
- 视图层传参完全错误:你在实例化序列化器时,给
data参数传入了字符串'profile.is_active',而不是请求携带的实际数据request.data,序列化器根本无法获取到请求传入的is_active值。另外你重复定义了两次同名的UpdateProfileActive类,属于冗余代码。 - 序列化器更新逻辑的真值判断错误:你在更新Profile字段时使用了
if is_active:这类判断,Python中False、空字符串''、None都属于假值,当你传入is_active=False时,判断条件不成立,赋值逻辑根本不会执行,自然无法将字段更新为False。同理is_online=False、city=''这类假值更新都会失效。
修复方案
1. 修正视图代码
删除重复的类定义,将序列化器的data参数改为请求数据,修正后代码:
from rest_framework import generics, Response class UpdateProfileActive(generics.UpdateAPIView): queryset = User.objects.all() serializer_class = UpdateUserSerializer http_method_names = ["patch"] # 仅允许PATCH请求,符合接口设计需求 def update(self, request, *args, **kwargs): instance = self.get_object() # 原代码此处传了字符串,改为传入请求实际数据 serializer = self.get_serializer(instance, data=request.data, partial=True) serializer.is_valid(raise_exception=True) serializer.save() return Response({"message": "user is_active field has been updated"})
2. 修正序列化器更新逻辑
去掉对字段值的真值判断,改为判断字段是否存在于待更新数据中,避免False、空字符串等合法假值被跳过:
def update(self, instance, validated_data): profile_data = validated_data.pop('profile', {}) instance = super().update(instance, validated_data) profile = instance.profile need_update = [] # 遍历所有Profile关联字段,只要传入了就更新,不管值是真还是假 for field in ["city", "country", "profile_pic", "is_online", "is_active"]: if field in profile_data: setattr(profile, field, profile_data[field]) need_update.append(field) if need_update: profile.save(update_fields=need_update) # 只更新传入的字段,提升性能 return instance
额外注意
发送请求时需要使用标准JSON格式:
- 布尔值使用小写的
true/false,不要用Python语法的True/False - 请求头携带
Content-Type: application/json,否则DRF可能无法正确解析请求体数据。
内容的提问来源于stack exchange,提问作者Emm
相关产品推荐
相关产品推荐

