python / expert
Snippet
Django Transaction Isolation Levels with Savepoint Management
This snippet demonstrates advanced Django transaction management with isolation levels and savepoint handling. The TransactionManager context manager provides fine-grained control over transaction behavior while the with_savepoint decorator enables nested transaction support for recoverable operations. The OrderProcessingService shows practical usage of select_for_update() for pessimistic locking, savepoint rollback for partial failures, and atomic blocks for grouped operations.
snippet.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
from django.db import transaction, connectionfrom django.db.transaction import Atomicfrom functools import wrapsimport logginglogger = logging.getLogger(__name__)class TransactionManager:def __init__(self, isolation_level=None):self.isolation_level = isolation_level or 'read committed'def __enter__(self):connection.set_autocommit(False)self.transaction = transaction.atomic()self.transaction.__enter__()return selfdef __exit__(self, exc_type, exc_val, exc_tb):if exc_type:self.transaction.__exit__(exc_type, exc_val, exc_tb)raise exc_valreturn self.transaction.__exit__(exc_type, exc_val, exc_tb)def with_savepoint(func):@wraps(func)def wrapper(*args, **kwargs):with transaction.savepoint():return func(*args, **kwargs)return wrapperclass OrderProcessingService:@with_savepointdef process_order(self, order_id, payment_data):from myapp.models import Order, Payment, Inventoryorder = Order.objects.select_for_update().get(id=order_id)with transaction.savepoint():inventory_check = Inventory.objects.select_for_update().filter(product_id=order.product_id,quantity__gte=order.quantity).exists()if not inventory_check:raise ValueError('Insufficient inventory')payment = Payment.objects.create(order=order,amount=order.total,**payment_data)with transaction.atomic(savepoint=True):Inventory.objects.filter(product_id=order.product_id).update(quantity=models.F('quantity') - order.quantity)order.status = 'processing'order.save()return orderdef bulk_process_with_recovery(self, order_ids):results = {'success': [], 'failed': []}for order_id in order_ids:try:with TransactionManager('repeatable read'):self.process_order(order_id, {})results['success'].append(order_id)except Exception as e:logger.error(f'Order {order_id} failed: {e}')results['failed'].append({'id': order_id, 'error': str(e)})return results
django
Breakdown
1
connection.set_autocommit(False)
Manually disable autocommit to control transaction boundaries explicitly in the context manager
2
with transaction.savepoint()
Create a savepoint within a transaction enabling partial rollback without aborting the entire transaction
3
select_for_update()
Acquire pessimistic lock on selected rows preventing concurrent modifications until transaction commits
4
transaction.atomic(savepoint=True)
Create atomic block that supports nested savepoints for complex operations that can partially fail
5
isolation_level='repeatable read'
Set transaction isolation level ensuring consistent reads within the same transaction session