Django中的游标是否在开放事务中运行?

时间:2012-07-19 06:07:25

标签: python sql django postgresql python-db-api

我的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。

2 个答案:

答案 0 :(得分:2)

我相信您需要一个单独的数据库连接才能获得单独的同步事务。我也很确定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装饰器中)并使用{{1}装饰里面缓存装饰器。它看起来很有趣,但我不知道为什么它不起作用:

    commit_on_success
  • 如果您使用模板缓存或更多涉及手动缓存,您还可以禁用@transaction.autocommit @cache_page(500) @transaction.commit_on_success def my_view(request): "..." (或将问题视图放在TransactionMiddleware装饰器中)并使用autocommit作为一个上下文管理器,只将您需要的代码放在托管事务中,而将其余视图保留为自动提交。

    commit_on_success

答案 1 :(得分:1)

如果有一个想要手动管理事务的视图,则应该使用该视图中的装饰器commit_manually。

来自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):
    ....

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