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

Django 5.x结合DRF、Djoser实现注册接口时出现500错误:Save with update_fields did not affect any rows

Django 5.x结合DRF、Djoser实现注册接口时出现500错误:Save with update_fields did not affect any rows

我帮你梳理下这个问题的来龙去脉,看起来是自定义User模型的主键生成逻辑和Djoser的注册流程在Django 5.x下产生了冲突。

从错误栈能定位到,问题出在Djoser序列化器调用user.save(update_fields=["is_active"])的环节——Django找不到要更新的数据库行。结合你的代码分析,核心原因是你用了PostgreSQL的RandomUUID函数作为主键默认值,这是数据库端生成的UUID,而非Python层面生成的:

# 你的models.py中的主键定义
user_id = models.UUIDField(primary_key=True, default=RandomUUID, editable=False)

当你的manager调用user.save()创建用户时,数据库会自动生成UUID并写入,但Django的User对象实例不会自动同步这个数据库生成的主键值——此时内存中的user对象user_id还是None。之后Djoser修改is_active字段并调用save(update_fields=["is_active"])时,Django会尝试更新user_id IS NULL的行,自然找不到匹配记录,就抛出了这个错误。

给你两个可行的解决方案,选其一即可:

方案1:改用Python层面生成UUID(推荐)

把主键默认值换成Python标准库的uuid.uuid4(),这样在创建User实例时就会生成UUID,无需依赖数据库生成,内存对象和数据库记录的主键会保持一致:

修改users/models.py的主键字段:

import uuid  # 记得顶部导入uuid模块
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import (
    AbstractBaseUser,
    PermissionsMixin
)

from .managers import UserAccountManager
from django.utils.translation import gettext_lazy as _


class UserAccount(AbstractBaseUser, PermissionsMixin):
    # 替换原来的RandomUUID为uuid.uuid4
    user_id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    # 其他字段保持不变...

之后重新执行数据库迁移(python manage.py makemigrations + python manage.py migrate),再测试注册接口应该就能正常工作了。

方案2:在manager中主动刷新对象数据

如果你坚持使用PostgreSQL的RandomUUID,那就在manager的创建方法中,保存用户后主动刷新对象,让内存中的实例同步数据库生成的主键:

修改users/managers.py的create_user和create_superuser方法:

from django.contrib.auth.models import BaseUserManager
from django.utils.translation import gettext_lazy as _


class UserAccountManager(BaseUserManager):
    def create_user(self, email, password=None, **kwargs):
        if not email:
            raise ValueError(_('Users must have an email address'))

        email = self.normalize_email(email)
        email = email.lower()

        user = self.model(
            email=email,
            **kwargs
        )

        user.set_password(password)
        user.save()
        # 新增这一行,从数据库刷新对象数据,获取生成的user_id
        user.refresh_from_db()
        return user

    def create_superuser(self, email, password=None, **kwargs):
        user = self.create_user(
            email,
            password=password,
            **kwargs
        )

        user.is_staff = True
        user.is_superuser = True
        user.save()
        # 同样新增刷新逻辑
        user.refresh_from_db()
        return user

这样Djoser拿到的User对象就有了正确的user_id,后续更新is_active时就能找到对应的数据库行。

额外提一句,Django 5.x对数据库操作的细节做了更严格的校验,这也是为什么教程用4.x没问题但5.x出问题的原因——4.x对这种主键未同步的情况容错性更高。

备注:内容来源于stack exchange,提问作者user3125823

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.14 15:17:57