Python @property内调用修改数据库数据的方法是否符合规范
结论
在@property修饰的属性方法里调用修改数据库的方法是完全错误的实现,必须把数据修改逻辑拆分出来,在明确的业务节点单独显式调用。
为什么这是坏实现
1. 直接违反@property的基本语义约定
Python里用@property的本质是把方法包装成属性访问的形式,所有Python开发者对「读属性」这个动作的默认预期是统一的:
- 只读不写,不会产生任何副作用
- 逻辑轻量,不会触发数据库、网络请求这类重IO操作
在属性访问逻辑里藏数据库写操作,等于完全打破了这个共识,会埋一堆根本没法排查的暗坑:比如调试的时候在IDE里扫了一眼对象属性、打日志的时候顺手读了下这个值、其他业务逻辑只是想查下用户有没有超限做个统计,都会悄无声息修改数据库里的数据,出了问题根本找不到是哪步触发的写入。
2. 业务逻辑触发时机完全不可控
当前代码里,计数重置是隐式触发的:只要任何地方读了这个属性,刚好距离上次更新过了10分钟,计数就被清了。
这等于直接给频控规则开了逻辑后门:哪怕根本没打算给用户发验证码,只是在无关逻辑里查了一句用户的发送状态,就直接把3次的计数清零了,10分钟的限制直接形同虚设。
顺带提一个显性的逻辑bug:当前代码调用reset_count_of_hashes_sent()的时候没传commit=True,只会修改内存里的变量值,根本不会把重置结果写进数据库,下次请求过来读到的还是旧计数,频控逻辑从根上就不对。
正确的改法
问题原始代码如下:
@property def check_too_much_sent_confirmation(self): if self._count_of_hashes_sent >= 3: if UTC.localize(datetime.now()) <= self.updated_at + timedelta(minutes=10): return True else: self.reset_count_of_hashes_sent() return False def reset_count_of_hashes_sent(self, commit=False): self._count_of_hashes_sent = 0 self._save_if_commit(commit)
核心改法是遵守命令查询分离原则:查询状态的方法只返回结果,绝对不修改数据;修改数据的方法单独拆分,只有在明确需要改数据的业务节点才调用。
参考改造逻辑:
from datetime import datetime, timedelta def is_send_confirmation_over_limit(self) -> bool: """纯查询方法:判断用户是否超过验证码发送频控,无任何副作用""" now = UTC.localize(datetime.now()) # 发送次数没到3次,直接返回未超限 if self._count_of_hashes_sent < 3: return False # 次数到了,判断是不是在10分钟的限制窗口里 return now <= self.updated_at + timedelta(minutes=10) def reset_count_of_hashes_sent(self, commit=True): """显式重置发送计数,默认直接落库""" self._count_of_hashes_sent = 0 self._save_if_commit(commit) def record_confirmation_send(self, commit=True): """记录一次验证码发送,统一处理窗口过期的计数重置""" now = UTC.localize(datetime.now()) # 已经过了10分钟窗口,先重置计数 if now > self.updated_at + timedelta(minutes=10): self.reset_count_of_hashes_sent(commit=False) # 累加发送次数,更新时间 self._count_of_hashes_sent += 1 self.updated_at = now self._save_if_commit(commit)
业务侧调用逻辑也会非常清晰,不存在任何隐式分支:
- 收到用户发送验证码的请求时,先调用
is_send_confirmation_over_limit()判断是否超限,超限直接返回提示 - 校验通过后,先调用
record_confirmation_send()记录本次发送(内部自动处理超窗重置、计数累加、落库),再实际下发验证码
内容的提问来源于stack exchange,提问作者user17252149
相关产品推荐
相关产品推荐

