我最近遇到需要在我的视图之一中禁用事务请求,以便能够在请求期间调用
db.connection.close()
和 connect()
以努力提高性能。
我有一个 DRF ViewSet,并使用以下非常简单的视图来验证 non_atomic_requests 装饰器似乎没有效果。
ATOMIC_REQUESTS=True
在 settings.py
和 DEBUG=False
中启用。
from django.db import transaction
@transaction.non_atomic_requests
def create(self, *args, **kwargs):
m = MyModel(stuff="hello")
m.save()
raise Exception('exception! row should still be saved though')
return Response()
调用视图后,我打开 Django shell,并验证数据库中的行数没有增加,尽管它应该增加。另外,在请求停止执行行
m.save()
之后打开调试器,我可以在 Django shell 中观察到新行尚不可见。
如果我在
ATOMIC_REQUESTS=False
中设置 settings.py
,代码将按预期工作,并且数据库中的行数会增加一,即使在从视图返回之前引发错误也是如此。
当
ATOMIC_REQUESTS=False
时,使用 @transaction.atomic
装饰器确实可以按预期工作。因此,作为一种解决方法,我可以使用它来将所有其他视图设置为原子视图......
我目前认为这是框架中的一个错误。任何人都可以验证我的发现,或者指出我是否误解了这个装饰器的功能?
我正在使用 Python 3.6、Django 2.0 和 DRF 3.7.7。
non_atomic_requests
仅在应用于视图本身时才有效。
在您的情况下,
create
是一种视图集方法,它不是视图本身。对于 Django 中基于常规类的视图,您需要使用 dispatch
包装
method_decorator
方法。
@method_decorator(transaction.non_atomic_requests, name='dispatch')
class MyViewSet(ViewSet):
...
def create(self, *args, **kwargs):
...
我对其余框架内部结构还不够熟悉,无法判断这是否有效。请注意,它将禁用视图集处理的all视图的原子请求,而不仅仅是
create
方法。
non_atomic_requests
方法有此限制,因为 Django 请求处理程序必须在运行之前检查视图,以便知道是否在事务中运行它。 transaction.atomic
装饰器没有相同的要求 - Django可以在进入原子函数或块时立即启动事务。
如果您使用“默认”以外的数据库: 您需要明确提及“使用”属性。 否则,它将默认为“默认”
transaction.non_atomic_requests(using='db_name')
如果是基于类的视图- 要么在视图中调度时应用它:
@method_decorator(transaction.non_atomic_requests(using='db_name'), name='dispatch')
class MyViewSet(ViewSet):
...
或将其应用于 url 中的 as_view 方法
path(r'endpoint/', transaction.non_atomic_requests(using='db_name')(MyViewSet.as_view()), name='myview')
使用 Django REST Framework ViewSet 时,您必须考虑实际 Django 路由将是在 ViewSet 上调用
.as_view({})
的结果。
因此,您需要装饰生成的基于类的视图的 .dispatch() 方法。鉴于
rest_framework.viewsets.ViewSet > rest_framework.views.APIView > django.views.generic.base.View
,并且 Django 文档明确提到了 View.dispatch()
方法,您应该知道您可以在 ViewSet 上使用 @method_decorator
。
在我正在研究的实现中,我们发现装饰调度方法也有效:
class MyView(APIView):
def post(self, request):
return Response("Hello!")
@transaction.non_atomic_requests()
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request, *args, **kwargs)
请注意,这将请求装饰所有方法,并且您必须在所有方法中手动处理事务。