2013-02-12 31 views
6

Hãy xem xét ví dụ đơn giản này:Làm việc với lồng nhau @ transaction.commit_on_success trong Django

# a bank account class 
class Account: 
    @transaction.commit_on_success 
    def withdraw(self, amount): 
     # code to withdraw money from the account 

    @transaction.commit_on_success 
    def add(self, amount): 
     # code to add money to the account 

# somewhere else 
@transaction.commit_on_success 
def makeMoneyTransaction(src_account, dst_account, amount): 
    src_account.withdraw(amount) 
    dst_account.add(amount) 

(lấy từ https://code.djangoproject.com/ticket/2227)

Nếu một ngoại lệ tăng trong Account.add(), giao dịch trong Account.withdraw() vẫn sẽ được cam kết và tiền bạc sẽ bị mất, bởi vì Django hiện không xử lý các giao dịch lồng nhau.

Không áp dụng bản vá cho Django, làm cách nào để đảm bảo rằng cam kết được gửi đến cơ sở dữ liệu, nhưng chỉ khi chức năng chính bên dưới trang trí @transaction.commit_on_success kết thúc mà không làm tăng ngoại lệ?

Tôi đã xem đoạn mã này: http://djangosnippets.org/snippets/1343/ và có vẻ như nó có thể thực hiện công việc. Có bất kỳ hạn chế nào tôi nên biết nếu tôi sử dụng nó?

Rất cảm ơn trước nếu bạn có thể trợ giúp.

P.S. Tôi đang sao chép đoạn mã được trích dẫn trước đây cho mục đích hiển thị:

def nested_commit_on_success(func): 
    """Like commit_on_success, but doesn't commit existing transactions. 

    This decorator is used to run a function within the scope of a 
    database transaction, committing the transaction on success and 
    rolling it back if an exception occurs. 

    Unlike the standard transaction.commit_on_success decorator, this 
    version first checks whether a transaction is already active. If so 
    then it doesn't perform any commits or rollbacks, leaving that up to 
    whoever is managing the active transaction. 
    """ 
    commit_on_success = transaction.commit_on_success(func) 
    def _nested_commit_on_success(*args, **kwds): 
     if transaction.is_managed(): 
      return func(*args,**kwds) 
     else: 
      return commit_on_success(*args,**kwds) 
    return transaction.wraps(func)(_nested_commit_on_success) 
+0

Tôi thậm chí không chắc chắn đoạn mã này sẽ hoạt động. Nó có vẻ là 4 tuổi bây giờ, và ai đó đang nói rằng nó không hoạt động ở đây: http://djangosnippets.org/snippets/2515/ – alexpirine

Trả lời

5

Vấn đề với đoạn mã này không cho phép bạn quay lại giao dịch bên trong mà không quay lại giao dịch bên ngoài. Ví dụ:

@nested_commit_on_success 
def inner(): 
    # [do stuff in the DB] 

@nested_commit_on_success 
def outer(): 
    # [do stuff in the DB] 
    try: 
     inner() 
    except: 
     # this did not work, but we want to handle the error and 
     # do something else instead: 

     # [do stuff in the DB] 

outer() 

Trong ví dụ trên, ngay cả khi inner() làm tăng ngoại lệ, nội dung của nó sẽ không được khôi phục.

Điều bạn cần là savepoint cho "giao dịch" bên trong. Đối với mã của bạn, nó có thể trông như thế này:

# a bank account class 
class Account: 
    def withdraw(self, amount): 
     sid = transaction.savepoint() 
     try: 
      # code to withdraw money from the account 
     except: 
      transaction.savepoint_rollback(sid) 
      raise 

    def add(self, amount): 
     sid = transaction.savepoint() 
     try: 
      # code to add money to the account 
     except: 
      transaction.savepoint_rollback(sid) 
      raise 

# somewhere else 
@transaction.commit_on_success 
def makeMoneyTransaction(src_account, dst_account, amount): 
    src_account.withdraw(amount) 
    dst_account.add(amount) 

Tính đến Django 1.6, atomic() trang trí không chính xác điều đó: nó sử dụng một giao dịch cho việc sử dụng ngoài của trang trí và sử dụng bất kỳ nội sử dụng một savepoint.

+1

Cảm ơn, giải pháp của bạn có vẻ thực sự an toàn hơn. Tôi hy vọng Django sẽ giới thiệu bộ trang trí @atomic sử dụng các điểm lưu trữ trong bản phát hành trong tương lai. Trong khi đó, tôi sẽ tiếp tục sử dụng của tôi vì nó đơn giản hơn để sử dụng. Nó vẫn sẽ hoạt động nếu bạn không bỏ qua các ngoại lệ được nêu ra từ các phương thức con. – alexpirine

2

Django 1.6 giới thiệu @atomic, thực hiện chính xác những gì tôi đang tìm kiếm!

Không chỉ hỗ trợ giao dịch "lồng nhau" mà còn thay thế giao diện trang trí cũ, ít mạnh mẽ hơn. Và nó là tốt để có một hành vi độc đáo và nhất quán cho quản lý giao dịch trên các ứng dụng Django khác nhau.

Các vấn đề liên quan