Django保存Account报NoneType无customer_id属性问题求助
问题根因
这次报错是两处逻辑错误直接导致的,另外还有三个隐藏bug,就算跑通首次创建流程后续也会触发异常:
- 直接触发本次报错的点:
utils.py中写的obj = Klass.objects.first(),你当前是第一次创建Account记录,表里是空的,first()会直接返回None,后续对空对象取属性自然抛出'NoneType' object has no attribute 'customer_id'错误。 - 核心逻辑偏差:你完全不需要从历史Account记录里取customer_id——当前要保存的Account实例,在admin表单提交时已经绑定了你选择的关联客户,直接从传入的
instance参数身上取关联客户id即可。原来的写法就算表里有数据,取到的也是全表第一条Account记录的客户id,和当前新建账户所属客户完全无关,生成的account_id全是错的。 - 隐藏bug1:就算取对了当前客户的id,如果这个客户之前没有开过户,查询他名下最新Account记录的结果也是None,直接取
.id会再次触发空对象错误。 - 隐藏bug2:customer_id、账户序号都是整数类型,直接用
+拼接会触发类型错误,必须先转成字符串才能拼接。 - 隐藏bug3:pre_save信号触发时,当前实例还没写入数据库,拿不到它自己的自增id,你现在用的“同客户下最新账户id+1”逻辑单线程运行没问题,并发量高了可能出现重复序号,最好加数据库级唯一约束兜底。
修复代码
先重写utils.py的逻辑:
def set_account_id(instance): account_model = instance.__class__ # 直接从当前待保存实例取绑定的客户ID,不需要查无关历史数据 current_customer_id = instance.customer_id # 查询当前客户名下最新的账户记录,没有则返回None last_account = account_model.objects.filter(customer_id=current_customer_id).order_by('id').last() if last_account: next_num = last_account.id + 1 else: # 客户名下第一个账户,序号从1开始 next_num = 1 # 统一转字符串后按规则拼接 return f"{current_customer_id}_{next_num}"
models.py里的pre_save绑定逻辑不需要改动,但是建议删掉顶部无用导入——from asyncio.windows_events import NULL这个导入完全没有实际作用,在非Windows环境下还可能触发导入错误。
优化建议
- 给
account_id字段加唯一约束,避免极端并发场景下生成重复的账户ID:
account_id = models.CharField(max_length=100, unique=True, default=None, blank=True, null=True)
- 如果你一定要用Account实例自身的自增id来拼接account_id,不能用pre_save信号,要换成post_save信号,但post_save是数据落库后才触发,生成account_id后需要再调用一次save(),会多一次数据库写入操作,效率不如当前的序号递增逻辑。
- 原代码里
Klass.objects.filter(customer_id=fk_value).all()是冗余写法,filter本身就返回查询集,不需要额外加.all()。
逻辑验证:修复后第一次给id=1的客户创建账户,会自动生成
1_1格式的account_id,同客户后续新建账户的序号依次递增,不会再触发空对象报错。
内容的提问来源于stack exchange,提问作者Manu Vats
相关产品推荐
相关产品推荐

