Add needs_price_update field to Purchase model
Django CI/CD / test (push) Successful in 22s
Django CI/CD / build-and-push (push) Has been skipped

Replace fragile price change detection in Purchase.save() with a
lazy dirty flag approach. A pre_save/post_save signal pair detects
price/currency changes without extra DB queries, and convert_prices()
uses the flag to determine which purchases need conversion.

- Add needs_price_update BooleanField with db_index
- Add pre_save signal to store old price/currency values
- Add post_save signal to set needs_price_update=True when price/currency changes
- Simplify Purchase.save() to remove DB reload + comparison logic
- Remove price_or_currency_differ_from() method
- Update convert_prices() to filter on needs_price_update flag
- Extract _get_exchange_rate() and _save_converted_price() helpers
- Add tests for the new behavior
This commit is contained in:
2026-05-12 13:57:59 +02:00
parent a4e697a274
commit e3b53cd4a9
5 changed files with 209 additions and 66 deletions
+108 -2
View File
@@ -1,3 +1,109 @@
from django.test import TestCase
from datetime import date
# Create your tests here.
from django.test import TestCase, override_settings
from games.models import Game, Platform, Purchase
from games.tasks import convert_prices
class PurchaseNeedsPriceUpdateTest(TestCase):
def setUp(self):
self.platform = Platform.objects.create(name="PC", icon="pc", group="PC")
self.game = Game.objects.create(name="Test Game", platform=self.platform)
def test_new_purchase_has_needs_price_update_true(self):
purchase = Purchase.objects.create(
price=50.0,
price_currency="USD",
date_purchased=date(2025, 1, 1),
)
purchase.games.add(self.game)
self.assertTrue(purchase.needs_price_update)
def test_convert_prices_sets_flag_to_false(self):
purchase = Purchase.objects.create(
price=50.0,
price_currency="USD",
date_purchased=date(2025, 1, 1),
)
purchase.games.add(self.game)
self.assertTrue(purchase.needs_price_update)
with override_settings(
CACHES={
"default": {
"BACKEND": "django.core.cache.backends.locmem.LocMemCache"
}
}
):
convert_prices()
purchase.refresh_from_db()
self.assertFalse(purchase.needs_price_update)
def test_price_change_sets_needs_price_update(self):
purchase = Purchase.objects.create(
price=50.0,
price_currency="USD",
date_purchased=date(2025, 1, 1),
)
purchase.games.add(self.game)
purchase.converted_price = 1000
purchase.converted_currency = "CZK"
purchase.needs_price_update = False
purchase.save()
purchase.price = 60.0
purchase.save()
purchase.refresh_from_db()
self.assertTrue(purchase.needs_price_update)
def test_currency_change_sets_needs_price_update(self):
purchase = Purchase.objects.create(
price=50.0,
price_currency="USD",
date_purchased=date(2025, 1, 1),
)
purchase.games.add(self.game)
purchase.converted_price = 1000
purchase.converted_currency = "CZK"
purchase.needs_price_update = False
purchase.save()
purchase.price_currency = "EUR"
purchase.save()
purchase.refresh_from_db()
self.assertTrue(purchase.needs_price_update)
def test_name_change_does_not_set_needs_price_update(self):
purchase = Purchase.objects.create(
price=50.0,
price_currency="USD",
date_purchased=date(2025, 1, 1),
)
purchase.games.add(self.game)
purchase.converted_price = 1000
purchase.converted_currency = "CZK"
purchase.needs_price_update = False
purchase.save()
purchase.name = "New Name"
purchase.save()
purchase.refresh_from_db()
self.assertFalse(purchase.needs_price_update)
def test_convert_prices_skips_already_converted(self):
purchase = Purchase.objects.create(
price=50.0,
price_currency="USD",
date_purchased=date(2025, 1, 1),
)
purchase.games.add(self.game)
purchase.converted_price = 1000
purchase.converted_currency = "CZK"
purchase.needs_price_update = False
purchase.save()
convert_prices()
purchase.refresh_from_db()
self.assertFalse(purchase.needs_price_update)