访问/profile/报MultipleObjectsReturned错误:get()返回2个CustomUser实例
根因分析
触发MultipleObjectsReturned异常的核心原因有3个,按出现概率从高到低排序:
ProfileView中认证类属性拼写错误,导致JWT认证未生效,认证阶段查询用户逻辑异常- 自定义用户管理器
MyUserManager的get_by_natural_key方法实现错误,未按唯一字段phone_number过滤用户 - 模型修改后未执行迁移,数据库层面
phone_number字段未真正建立主键/唯一约束,存在重复手机号的用户数据
修复步骤
1. 修正ProfileView的错误写法
首先修正认证类属性拼写(单数改复数),同时调整异常捕获的位置到方法内部:
class ProfileView(APIView): # 原拼写错误authentication_class,修正为复数 authentication_classes = [JWTAuthentication] permission_classes = [IsAuthenticated] def get(self, request): try: user = model_to_dict(request.user) del user['password'] return Response({"ok": True, "userData": user}, status=200) except Exception as e: print("::",e,"::") return Response({"ok": False, "message": str(e)}, status=500)
2. 修正MyUserManager实现
你自定义了用户管理器,且指定USERNAME_FIELD = 'phone_number',必须保证get_by_natural_key方法按手机号唯一过滤用户,参考正确实现:
from django.contrib.auth.base_user import BaseUserManager class MyUserManager(BaseUserManager): def create_user(self, phone_number, password=None, **extra_fields): if not phone_number: raise ValueError('手机号为必填项') extra_fields.setdefault('is_staff', False) extra_fields.setdefault('is_superuser', False) user = self.model(phone_number=phone_number, **extra_fields) user.set_password(password) user.save(using=self._db) return user def create_superuser(self, phone_number, password=None, **extra_fields): extra_fields.setdefault('is_staff', True) extra_fields.setdefault('is_superuser', True) return self.create_user(phone_number, password, **extra_fields) # 该方法是Django认证系统查询用户的核心方法,必须按USERNAME_FIELD过滤 def get_by_natural_key(self, phone_number): return self.get(**{self.model.USERNAME_FIELD: phone_number})
3. 校验数据库约束与数据
先执行以下代码检查是否存在重复手机号的用户:
# 进入Django shell执行 from django.db.models import Count from yourapp.models import CustomUser # 替换为你自己的app名 # 查询重复手机号 duplicate_phones = CustomUser.objects.values('phone_number').annotate(count=Count('phone_number')).filter(count__gt=1) print(duplicate_phones)
如果有返回结果,说明数据库存在重复数据:
- 先手动清理重复的用户记录
- 重新执行
python manage.py makemigrations和python manage.py migrate,确保phone_number的主键、唯一约束在数据库层面生效
4. 注册逻辑优化(可选)
你当前注册逻辑中「判断手机号是否存在」和「创建用户」两步操作存在竞态条件,高并发场景下仍可能插入重复数据,建议直接使用get_or_create或者加数据库事务包裹创建逻辑。
内容的提问来源于stack exchange,提问作者printf-twinkle
相关产品推荐
相关产品推荐

