Django 装饰器 @transaction.non_atomic_requests 无法在 ViewSet 方法中工作

问题描述 投票:0回答:3

我最近遇到需要在我的视图之一中禁用事务请求,以便能够在请求期间调用

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。

python django transactions django-rest-framework
3个回答
16
投票

如文档所述

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可以在进入原子函数或块时立即启动事务。


0
投票

如果您使用“默认”以外的数据库: 您需要明确提及“使用”属性。 否则,它将默认为“默认”

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')

0
投票

使用 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)

请注意,这将请求装饰所有方法,并且您必须在所有方法中手动处理事务。

© www.soinside.com 2019 - 2024. All rights reserved.