python 40 lines · 8 steps

Batch price updates safely in Django

Apply a percentage markup to every active product in a category using precise decimals, row locks, and batched writes inside one transaction.

Explained by highlit
1from decimal import Decimal, ROUND_HALF_UP
2 
3from django.db import transaction
4from django.db.models import F
5 
6from catalog.models import Product
7 
8 
9def apply_category_markup(category_id, percent_increase, *, batch_size=500):
10 factor = Decimal(1) + (Decimal(percent_increase) / Decimal(100))
11 cent = Decimal("0.01")
12 
13 products = (
14 Product.objects
15 .select_for_update()
16 .filter(category_id=category_id, is_active=True)
17 .only("id", "price", "list_price")
18 )
19 
20 updated = []
21 with transaction.atomic():
22 for product in products.iterator(chunk_size=batch_size):
23 new_price = (product.price * factor).quantize(cent, rounding=ROUND_HALF_UP)
24 if new_price == product.price:
25 continue
26 product.price = new_price
27 product.list_price = max(new_price, product.list_price)
28 updated.append(product)
29 
30 if len(updated) >= batch_size:
31 Product.objects.bulk_update(updated, ["price", "list_price"], batch_size=batch_size)
32 updated.clear()
33 
34 if updated:
35 Product.objects.bulk_update(updated, ["price", "list_price"], batch_size=batch_size)
36 
37 Product.objects.filter(category_id=category_id, is_active=True).update(
38 price_updated_at=F("updated_at"),
39 revision=F("revision") + 1,
40 )
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Use Decimal with explicit quantize and rounding to avoid float errors in money math.
  2. 2select_for_update plus a single atomic block prevents concurrent writers from corrupting price updates.
  3. 3Streaming with iterator and flushing bulk_update in batches keeps memory flat over large tables.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Batch price updates safely in Django — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code