Django 中的游标是不是在打开的事务中运行?

Posted

技术标签:

【中文标题】Django 中的游标是不是在打开的事务中运行?【英文标题】:Do cursors in Django run inside the open transaction?Django 中的游标是否在打开的事务中运行? 【发布时间】:2012-07-18 06:48:32 【问题描述】:

我的 Django 应用程序正在使用一些自定义 SQL,我在这样的视图中执行:

db = router.db_for_write(model)
cursor = connections[db].cursor()
cursor.execute("INSERT INTO ....")

由于我使用的是TransactionMiddleware,因此我的视图在事务中运行,但我不清楚获取这样的新游标是否会“转义”当前打开的事务,或者游标是否仍然是事务的一部分打开交易。我收到一些错误消息,让我相信游标正在事务中运行。

我希望能够使用游标在TransactionMiddleware 打开的事务之外执行 SQL 命令。这可能吗?

如果重要的话,我正在运行带有 PostgreSQL 8.4 数据库的 Django 1.4。

【问题讨论】:

【参考方案1】:

我相信您需要单独的数据库连接才能获得单独的同时事务。我也很确定 django 每个数据库只管理一个连接。但是你可以创建另一个。可能有一些很好的理由不这样做。想到了复杂性。

我认为这样的事情会起作用:

from django.conf import settings
from django.db.utils import ConnectionHandler

def my_view(request):
    """Flirt with complexity by using two connections to db"""
    private_connections = ConnectionHandler(settings.DATABASES)
    db = router.db_for_write(model)
    new_conn = private_connections[db]
    new_conn.enter_transaction_management()
    new_conn.managed(True)
    new_cur = new_conn.cursor()
    new_cur.execute("INSERT INTO ...")
    new_conn.commit()
    new_conn.close()

请注意,您不能使用 django.db.transaction,因为它在 django.db.connections 中的全局连接实例上运行,但无论如何,这只是连接对象上事务管理方法的一个薄包装。

我想真正的问题是你为什么要这样做?! Lakshman Prasad 的回答有什么问题?您可以随时提交/回滚,因此没有什么可以阻止您在单个视图中的不同事务中执行不同的任务。事务必须是并行的而不是连续的这一事实暗示了它们之间的某种逻辑联系,在我看来,这表明它们确实应该在同一个事务中。

另一方面,如果您只是想模拟某种离线处理,其成功或失败与视图根本无关,请考虑设置消息队列并执行这些插入在一个单独的过程中。 Celery 是一个流行的包。但是,如果响应时间不是主要问题,我仍然认为连续事务就足够了。

更新:

如果您希望数据库支持的缓存在自动提交模式下运行,同时仍然在单个(单独的)事务中运行您的业务逻辑,那么有一种 django 方式。您需要做的就是确保缓存发生在外部commit_on_success:

如果您只是使用缓存中间件,请确保它位于 TransactionMiddleware 之外。

如果您使用缓存视图装饰器,我敢猜测您可以禁用TransactionMiddleware(或将问题视图放在autocommit 装饰器中)并使用commit_on_success 装饰器inside 缓存装饰器。它看起来很有趣,但我不知道为什么它不起作用:

@transaction.autocommit
@cache_page(500)
@transaction.commit_on_success
def my_view(request):
    "..."

如果您使用模板缓存或进行更多涉及的手动缓存,您还可以禁用TransactionMiddleware(或将问题视图放在autocommit 装饰器中)并使用commit_on_success 作为上下文管理器来仅放置您的代码在托管事务中需要,将视图的其余部分留在自动提交中。

@transaction.autocommit
def my_view(request):
    data = cache.get(some_key)
    with transaction.commit_on_success():
        context = do_some_processing(data)
    cache.set(some_key, context['data'])
    return render('template/with/cache/blocks.html', context=context)

【讨论】:

感谢您的帮助。在查看了 Django 源代码后,我得出了同样的结论,即我需要创建另一个数据库连接。我正在将 Django 缓存与数据库后端一起使用,并试图找出一个间歇性问题,我认为该问题是由不同事务同时更新缓存引起的。由于缓存不需要在事务中保持一致,我想将缓存事务移动到自动提交。 好的。我仍然不确定我是否完全理解这如何或为什么会有所帮助,这让我认为这仍然不是你真正想要的,但我已经用更简单的方法更新了我的答案,以将缓存查询排除在业务逻辑事务之外.【参考方案2】:

如果有要手动管理事务的视图,则应使用该视图中的装饰器来手动提交。

来自documentation。

from django.db import transaction

@transaction.commit_manually
def viewfunc(request):
    ...
    # You can commit/rollback however and whenever you want
    transaction.commit()
    ...

    # But you've got to remember to do it yourself!
    try:
        ...
    except:
        transaction.rollback()
    else:
        transaction.commit()

@transaction.commit_manually(using="my_other_database")
def viewfunc2(request):
    ....

是的,导入事务游标只提供事务的游标,并不会创建新事务。

【讨论】:

谢谢。我想要做的是继续在事务中运行我的视图,但也打开一个到数据库的单独连接,我可以在视图的事务之外执行 SQL。这可能吗?如果我用@transaction.commit_manually 或 autocommit 在我的视图中装饰一个函数,那会起作用吗? 问题似乎是您是否可以打开不在当前事务中的连接。你的答案是你可以用当前的交易做什么。

以上是关于Django 中的游标是不是在打开的事务中运行?的主要内容,如果未能解决你的问题,请参考以下文章

Django数据库--事务及事务回滚

在 python (django) 中使用带有参数的游标/查询中的“like”

SQL Server 事务异常和游标

在 django 中创建原子事务是不是会自动创建锁

SQL Server 事务异常和游标

PL/SQL:clob 字符串中的动态查询。如何打开游标?