bounty还有6天到期。回答此问题可获得+200声望奖励。Kevin Renskers希望奖励现有答案。
我有一个Django REST Framework序列化器,它使用select_for_update
和原子转换,如下所示:这https://docs.djangoproject.com/en/4.2/ref/models/querysets/#select-for-update/很好,除了我想在抛出错误时向数据库写入一些东西......这些insert语句被回滚,永远不会到达数据库。
代码是这样的(非常简化,但得到了点,是可复制的):
models.py
from django.db import models
class LicenseCode(models.Model):
code = models.CharField(max_length=200, unique=True)
class Activation(models.Model):
license_code = models.TextField(max_length=200)
activation_log = models.TextField(blank=True, null=True)
success = models.BooleanField(default=False)
views.py
from django.http import HttpResponse, Http404
from django.db import transaction
from .models import Activation, LicenseCode
class LicenseRedeemSerializer:
@transaction.atomic
def save(self):
license_codes = LicenseCode.objects.all().select_for_update()
activations = []
for license_code in license_codes:
activations.append(
Activation(license_code=license_code.code, success=False)
)
self.activate_licenses(activations)
def activate_licenses(self, activations):
try:
# In our real app we'd try to activate the licenses with an external SDK. This can fail.
raise Exception("Something went wrong!")
except Exception as e:
for activation in activations:
activation.activation_log = str(e)
activation.save()
# With our real DRF serializer we'd raise ValidationError
raise Http404("Could not activate the license!")
def view(request):
# Let's make sure we have a license code to work with
LicenseCode.objects.get_or_create(code="A")
serialier = LicenseRedeemSerializer()
serialier.save()
html = "Hello there"
return HttpResponse(html)
我面临的问题是,当外部SDK触发错误时,我试图向数据库写入一些内容,这永远不会在数据库中结束,事务只是回滚。
如何确保在使用原子事务时,在except块中仍然可以向数据库写入内容?
2条答案
按热度按时间gwo2fgha1#
正如
atomic()
文档中提到的,它将在异常时回滚事务,因此我认为没有办法在错误期间直接将信息存储在数据库中。但是你总是可以在原子块之外捕获异常,然后保存错误并像这样重新引发错误:
或者,您可以创建一个对象变量(如列表),您可以将SDK中发生的异常放入其中,然后将这些错误存储在DB中。
通常,日志不存储在数据库中,而是存储在文件中。您可以考虑将错误存储在文件系统中或使用Django's logging来存储错误,而不是将错误存储在数据库中。
更新
你可以删除允许回滚发生的嵌套异常引发。相反,你可以使用一个标志来在以后引发404。
或者,您可以查看保存点,但我不确定它如何适用于您的代码。
knpiaxh12#
你可以使用context manager来代替decorator。例如: