Django自定义手机号用户模型外键关联时User匹配查询不存在报错
Django自定义用户模型外键关联报错解决方案
报错核心原因排查与解决
- 视图逻辑顺序错误,未校验用户存在就直接查询
当前代码先执行User.objects.get(phone_number=phone_number),虽然提前做了number_exists存在性判断,但完全没有处理不存在的分支,只要传入的手机号无对应用户就会直接抛出DoesNotExist错误。
修复方法:优先做参数校验,再用try-except捕获不存在的情况,返回友好响应。
- 视图逻辑顺序错误,未校验用户存在就直接查询
- 直接取未校验的请求参数,值不符合预期
从request.data直接拿参数,未经过序列化器校验,可能出现phone_number为空、拼写错误、携带多余空格/国家码的情况,和数据库存储的格式不匹配导致查不到用户。
修复方法:把serializer.is_valid(raise_exception=True)放到参数取值前,从校验后的serializer.validated_data里取值,同时对手机号做去空格处理。
- 直接取未校验的请求参数,值不符合预期
- 交易模型外键默认值配置错误
TransactionModel中关联用户的外键设置了default=1,但你指定了to_field="phone_number",关联字段是手机号,默认值1不是合法的手机号,保存交易记录时如果没传入用户值,会自动查询phone_number=1的用户,直接触发报错。
修复方法:删除错误的default配置,要么设置null=True, blank=True允许外键为空,要么保存序列化器时主动传入关联的用户对象。
- 交易模型外键默认值配置错误
- 自定义用户模型缺失必要配置
继承AbstractBaseUser的自定义用户模型必须指定USERNAME_FIELD为你用来做唯一标识的字段,否则Django默认会用username作为用户标识,可能引发隐式查询错误。
修复方法:在User模型中添加配置项:
- 自定义用户模型缺失必要配置
USERNAME_FIELD = 'phone_number'
修正后参考代码
调整后的用户模型
class User(AbstractBaseUser, PermissionsMixin): phone_number = models.CharField("Phone number", max_length=100, unique=True, error_messages={ 'unique': "number_existsssss", }) name = models.TextField("Name") time_stamp = models.DateTimeField("Time-Date", auto_now=True) # 补全原代码缺失的右括号 request_type = models.CharField(max_length=100, default="check") is_staff = models.BooleanField(verbose_name='staff status', default=False) is_active = models.BooleanField(verbose_name='active', default=True) # 补充余额字段(原视图用到但未定义) current_balance = models.DecimalField(max_digits=10, decimal_places=2, default=0) # 声明唯一标识字段 USERNAME_FIELD = 'phone_number'
调整后的交易模型
class TransactionModel(models.Model): trxID = models.CharField(max_length=15, null=True, unique=True) # 删除错误的default=1,根据业务需要选择是否允许为空,字段名改用小写蛇形避免和类名混淆 user = models.ForeignKey( to=User, to_field="phone_number", related_name='transactions', on_delete=models.DO_NOTHING, null=True, blank=True ) time = models.DateTimeField(auto_now=True, null=True) amount = models.CharField(max_length=5, null=True) pay_method = models.CharField(max_length=10, null=True) amountReceivedFrom = models.CharField(max_length=26, null=True) transactionStatus = models.CharField(max_length=15, null=True, default='False')
调整后的视图代码
class BkashCashinView(APIView): @transaction.atomic def post(self, request, *args, **kwargs): HEADERS = { "Access-Control-Allow-Origin": "*" } # 优先做参数校验 serializer = BkashCashinSerializer(data=request.data) serializer.is_valid(raise_exception=True) req = serializer.validated_data trx_id = req.get('trxID') # 去空格避免多余字符导致查询失败 phone_number = req.get('phone_number', '').strip() recharge_amount = float(req.get('amount')) # 捕获用户不存在的情况 try: current_user_data = User.objects.select_for_update().get(phone_number=phone_number) except User.DoesNotExist: return Response({ "status": "fail", "msg": "对应用户不存在" }, status=400, headers=HEADERS) # 更新余额 current_user_data.current_balance += recharge_amount current_user_data.save() # 保存交易时主动传入关联用户,避免触发默认值查询 serializer.save(user=current_user_data) context = { "current_balance": current_user_data.current_balance, "status": "success" } return Response(context, headers=HEADERS)
内容的提问来源于stack exchange,提问作者Sadekur Rahaman
相关产品推荐
相关产品推荐

