我正在使用DRF、基于API视图类的视图、post方法。 参数为:文件 逻辑:对文件进行一些验证并逐步保存文件(不同类型的对象) 如果在保存文件的其余部分时发生异常,我正在尝试回滚事务。我设置
'ATOMIC_REQUESTS'
:True
class SaveXMlFile(APIView):
authentication_classes = [TokenAuthentication]
permission_classes = [IsAuthenticated]
parser_classes = [FormParser, MultiPartParser]
def post(self, request):
"""
Save xml file
---
# inputs
parameters:
- name: game_log_file
description: Game log file
type: file
required: true
paramType: post
allowMultiple: false
"""
try:
# import pdb; pdb.set_trace()
game_log_file = request.data['game_log_file']
file_store = FileStore.objects.create(uploaded_file=game_log_file)
xml_file_processing = ProcessXmlFile(file_store)
already_saved = xml_file_processing.was_file_saved()
player_exists = xml_file_processing.player_exists()
if already_saved:
file_store.delete()
return Response({"info": "File was saved previously, no action taken place this time."}, status=200)
if not player_exists:
file_store.delete()
return Response({"info": "No player exists in the database, ask your administrator to create some."}, status=200)
xml_file_processing.save()
file_store.delete()
return Response({"success": "File has been saved."}, status=status.HTTP_201_CREATED)
except Exception as err:
error = "{0}".format(str(err))
return JsonResponse({'exception': error}, status=500)
当文件的一半已保存时,我故意抛出异常,但即使在过程中引发异常,已提交的事务也不会回滚。
如有任何想法,我们将不胜感激。
您应该阅读更多有关 Django 事务如何工作的内容。 由于您捕获了异常,Django 将看到一切正常并将提交事务,无论您的响应代码是什么。取自https://docs.djangoproject.com/en/1.10/topics/db/transactions/#tying-transactions-to-http-requests:
它的工作原理是这样的。在调用视图函数之前,Django 会启动一个 交易。如果响应生成没有问题,Django 提交交易。如果视图产生异常,Django 回滚事务。
因此,既然您捕获了异常并返回响应,Django 就认为没有理由执行回滚。
这里有两个独立的东西。首先,当发生错误时,DRF 会自行引发 500 响应(因此无需捕获异常并显式引发 500)。其次,您也可以考虑像这样应用 method_decorator :
from django.utils.decorators import method_decorator
from django.db.transaction import atomic
@method_decorator(atomic, name='dispatch')
class MyView(APIView):
....
这背后的主要原理是,由于性能损失,使用设置为 True 的 ATOMIC_REQUESTS 可能并不总是理想的,但您仍然可能希望将某些特定视图包装在原子块中。当然,在这种情况下你可以使用
with atomic():
... your code
因为你可以访问该方法本身,但这有点离题:)
希望这有帮助
致未来的访客(假设时间旅行)
根据经验...
进行交易时切勿使用 try...catch!因为,通过事务,您正在尝试解决不一致的问题......但是,通过 try...catch,您隐藏了不一致,从而导致事务无法回滚。
对我有用的一件事是回滚 except 块中的事务。考虑这个受您的代码片段启发的示例 -
from django.db import transaction #--> import this
class SaveXMlFile(APIView):
authentication_classes = [TokenAuthentication]
permission_classes = [IsAuthenticated]
parser_classes = [FormParser, MultiPartParser]
@transaction.atomic # --> add this
def post(self, request):
try:
# import pdb; pdb.set_trace()
game_log_file = request.data['game_log_file']
file_store = FileStore.objects.create(uploaded_file=game_log_file)
xml_file_processing = ProcessXmlFile(file_store)
already_saved = xml_file_processing.was_file_saved()
player_exists = xml_file_processing.player_exists()
if already_saved:
file_store.delete()
return Response({"info": "File was saved previously, no action taken place this time."}, status=200)
if not player_exists:
file_store.delete()
return Response({"info": "No player exists in the database, ask your administrator to create some."}, status=200)
xml_file_processing.save()
file_store.delete()
return Response({"success": "File has been saved."}, status=status.HTTP_201_CREATED)
except Exception as err:
transaction.set_rollback(True) # --> add this
error = "{0}".format(str(err))
return JsonResponse({'exception': error}, status=500)
这背后的逻辑是,一旦你尝试并除非你已经将其标记为原子,代码就会认为你可能已经处理了错误并且不会自动回滚。因此,您必须显式地执行此操作,从而将 set_rollback 标记为 true。