在django原子事务中提交特定的保存操作

时间:2015-07-06 19:09:35

标签: python django postgresql

我有一个用原子事务装饰的视图。但是,我总是希望在此视图中保存特定操作。操作是调用第三方API,有时我需要在应用程序发出请求之前刷新令牌。但是,如果在刷新之后出现故障,则新令牌不会被保存而是回滚。如何始终将保存操作提交到数据库?我已经考虑使用savepoint,但我不确定我是否在正确的轨道上。

views.py

@transaction.atomic
def my_view(request)
    from another_file import fx
    a = Mymodel.objects.first()
    fx(a)

    return a

another_file.py

   def fx(obj) 
       from django.db import transaction

       sid = transaction.savepoint()

       obj.token = 'jfkds'
       obj.save()
       transaction.savepoint_commit(sid)

       raise Exception

2 个答案:

答案 0 :(得分:1)

遇到类似问题,过程B'过程B需要用户令牌。验证从过程A'过程中传递的身份验证在进程A中的事务已经提交之前:

要解决这个问题,我需要' Token.objects.get_or_create(user = user)'无论是否已经有一些正在进行的周边交易,都要求在自己的交易中提交。我认为您正在查看与您的问题相同的问题。

我使用单独的数据库连接进行令牌处理。因此在设置中:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'mydb',
        'USER': 'mydbuser',
        'PASSWORD': DBPASSWORD}}
DATABASES['security'] = DATABASES['default']  # allow independent security transactions/connections
DATABASE_ROUTERS = ['security.db_router.SecurityRouter']

路由器确保在“安全性”中处理令牌模型。数据库。您可以将路由器放在您喜欢的位置,并使用上述路由器设置将其链接。:

from rest_framework.authtoken.models import Token


class SecurityRouter(object):

    def db_for_read(self, model, **hints):
        return self.mine(model, **hints)

    def db_for_write(self, model, **hints):
        return self.mine(model, **hints)

    def allow_relation(self, obj1, obj2, **hints):
        return True

    def allow_migrate(self, db, app_label, model=None, **hints):
        return True

    @staticmethod
    def mine(model, **hints):
        if model == Token:
            return 'security'

所有Token对象的使用现在都发生在' security'数据库。因此,用户令牌是在独立于您的默认交易的交易中创建的。数据库事务,它是立即的,就像所有django连接一样(除非你另有说明)。

答案 1 :(得分:0)

或者,稍微容易一点,您可以先保存并更新令牌,然后使用with块使其余的视图操作成为原子:

# @transaction.atomic
def my_view(request)
    from another_file import fx
    with transaction.atomic():
        # this has to be atomic to avoid write-after-read conflicts
        a = Mymodel.objects.first()
        fx(a)
    with transaction.atomic():
        # do the rest of your view operations here including another get to Mymodel.objects.first()
        a = Mymodel.objects.first()
        # rest of view stuff goes here

    return a