如何在 Django 或 DRF 中实现这样的自定义回滚?

How to implement such a custom rollback in Django or DRF?

out系统访问一个中间平台(英文不知道怎么说,我们中文叫中台),做登录,JWT验证等认证,等等 然后,当由于意外的程序错误而需要回滚操作时,我们遇到了问题。如下代码,当运行在1 / 0时程序会崩溃,然后AdminPermission.objects.create可以回滚,但是do_user_actions不能回滚,因为它是一个RPC函数。所以,我们需要覆盖 transaction.atomic 或类似的东西来实现我们的要求。但是,我不知道如何实施它。请给我一些建议或示例代码。非常感谢。

    @transaction.atomic  # can not rollback remote func call `do_user_actions`
    def create(self, request, *args, **kwargs):
        # call a remote func here to create a admin account.
        user_dict = do_user_actions(query_kwargs=query_kwargs, action='create-admin')  

        user_id = user_dict.get('id')
        permission_code = 'base_permission'
        AdminPermission.objects.create(user_id=user_id, permission_code=permission_code)

        # some unexpected errors, like:
        1 / 0
        
        return Response('success')

您可以将装饰器用作上下文管理器,而不是将 atomic 与装饰器一起使用,就像这样,

with transaction.atomic():
  try:
    # do your stuff
    user_dict = do_user_actions(query_kwargs=query_kwargs, action='create-admin')
    user_id = user_dict.get('id')
    permission_code = 'base_permission'
    AdminPermission.objects.create(user_id=user_id, permission_code=permission_code)
    1/0 # error
  except SomeError: # capture error
    revert_user_actions() # revert do_user_actions
    transaction.set_rollback(True) # rollback
    return Response(status=status.HTTP_424_FAILED_DEPENDENCY) # failure
return Response(serializer.data, status=status.HTTP_201_CREATED) # success

Django Docs - Transactions

更新

正如@Gorgine 在评论中提到的,文档不建议处理 atomic 中的错误。因为遵循建议总是一个好主意,所以您可以将 atomic 放在 try 块中。在这种情况下,如果发生错误,atomic 块将处理回滚,因此您需要在 except 块中处理 atomic 未回滚的操作。像这样:

try:
  with transaction.atomic():
    # do your stuff
    user_dict = do_user_actions(query_kwargs=query_kwargs, action='create-admin')
    user_id = user_dict.get('id')
    permission_code = 'base_permission'
    AdminPermission.objects.create(user_id=user_id, permission_code=permission_code)
    1/0 # error
except SomeError: # capture error
  revert_user_actions() # revert do_user_actions
  return Response(status=status.HTTP_424_FAILED_DEPENDENCY) # failure
return Response(serializer.data, status=status.HTTP_201_CREATED) # success