我有一个 ATOMIC_REQUESTS=True 的 Django 项目。
当身份验证失败时,我需要存储登录尝试的关键数据,这里是示例代码:
class LoginSerializer(serializers.Serializer):
...
def validate(self, data):
...
user = authenticate(email=email, password=password)
if user is None:
failed_login_attempt(email)
raise serializers.ValidationError("Invalid credentials")
def failed_login_attempt(email):
user = User.objects.get(email=email)
user.lock_status = "LOCKED"
user.save()
Device.objects.create(user=user, ip="127.0.0.1", fingerprint="some-fingerprint")
Activity.objects.create(user=user, type="failed_login_attempt")
限制:
引发 ValidationError 会导致所有更改(包括对 user.lock_status、设备和活动的更新)回滚。由于全局事务,此行为是预期的,但我需要保留这些更改。
我无法删除 ATOMIC_REQUESTS=True,因为它对于应用程序的其他部分至关重要。
目标:
我想确保即使 ValidationError 回滚事务的其余部分,
failed_login_attempt
函数也会持续更改。坚持这些关键变化的最佳方法是什么?
我尝试过的:
将
failed_login_attempt
包装在 transaction.atomic()
中,这不起作用,因为它们仍然是外部事务的一部分。
使用 ATOMIC_REQUESTS=False 的单独数据库配置和自定义数据库路由器,如本asnwer所示,但是:
这需要其他模型(用户、设备)的广泛权限,这将允许在全局事务之外进行写入。 我无法将这种方法仅限于特定功能(failed_login_attempt),从而导致控制范围过于广泛。
绕过 ORM 并使用原始 SQL
connections.cursor()
感觉像是一种 hack,所以我宁愿避免它。
似乎您可以通过在要保留更改的每个视图上使用
django.db.transaction.non_atomic_requests
装饰器来解决此问题。
此装饰器将否定给定视图的 ATOMIC_REQUESTS 的效果。