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

8

我最近在一个视图中遇到了需要禁用事务请求的需求,以便能够在请求期间调用 db.connection.close()connect() 以提高性能。

我有一个DRF视图集,并使用以下非常简单的视图来验证 non_atomic_requests 装饰器似乎没有任何效果。在 settings.py 中启用了 ATOMIC_REQUESTS=True,并且 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中观察到新行尚不可见。
如果在settings.py中设置ATOMIC_REQUESTS=False,则代码按预期工作,并且即使从视图返回之前出现错误,数据库中的行数也会增加一个。
ATOMIC_REQUESTS=False时,使用@transaction.atomic修饰符按预期工作。因此,我可以使用它将其他视图设置为原子视图以解决问题...
我目前认为这是框架中的一个bug。有人能验证我的发现或指出我是否误解了此修饰符的功能吗?
我使用的是Python 3.6、Django 2.0和DRF 3.7.7。

由于之前的异常抛出,导致程序永远无法执行到返回语句。 - Yassine Faris
2个回答

14

按文档所述non_atomic_requests 只在应用于视图本身时才起作用。

在您的情况下,create 是一个视图集方法,而不是视图本身。在 Django 中使用常规的基于类的视图,您需要使用method_decorator 包装 dispatch 方法。

@method_decorator(transaction.non_atomic_requests, name='dispatch') 
class MyViewSet(ViewSet):
    ...

    def create(self, *args, **kwargs):
        ...

我对REST框架的内部不够熟悉,无法确定这是否有效。请注意,它将禁用视图集处理的所有视图的原子请求,而不仅仅是create方法。

non_atomic_requests方法有此限制,因为Django请求处理程序必须在运行视图之前检查视图,以便知道是否在事务中运行。 transaction.atomic装饰器没有相同的要求-当Django进入原子函数或块时,可以立即开始事务。


有人确认这个方法适用于Django Rest Framework ViewSets吗?我尝试过但没有成功。 - Joshua Swain
@JoshuaSwain 我认为这应该可以工作,因为ViewSet从dispatch方法此处复制属性。我还没有尝试确认它是否可以工作。 - Alasdair

0

如果您使用的是除“default”之外的其他db: 您需要明确指定“using”属性。 否则,它将默认为“default”。

transaction.non_atomic_requests(using='db_name')

对于基于类的视图 - 在视图中调用dispatch方法:

@method_decorator(transaction.non_atomic_requests(using='db_name'), name='dispatch') 
class MyViewSet(ViewSet):
...

或者将其应用在urls中的as_view方法上

path(r'endpoint/', transaction.non_atomic_requests(using='db_name')(MyViewSet.as_view()), name='myview')

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接