6

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

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

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

我希望能够使用游标在由TransactionMiddleware. 这可能吗?

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

4

2 回答 2

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装饰器中)并在缓存装饰器中使用装饰commit_on_success。它看起来很有趣,但我不知道为什么它不起作用:

    @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)
    
于 2012-09-11T14:42:56.150 回答
2

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

文档中。

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

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

于 2012-07-19T06:53:21.627 回答