CartController now uses BatchController memoisation

This commit is contained in:
Christopher Neugebauer 2016-05-01 12:19:36 +10:00
parent 3717adb262
commit 3d635521eb

View file

@ -16,6 +16,7 @@ from registrasion.models import commerce
from registrasion.models import conditions
from registrasion.models import inventory
from.batch import BatchController
from .category import CategoryController
from .discount import DiscountController
from .flag import FlagController
@ -34,10 +35,11 @@ def _modifies_cart(func):
def inner(self, *a, **k):
self._fail_if_cart_is_not_active()
with transaction.atomic():
with CartController.operations_batch(self.cart.user) as mark:
mark.mark = True # Marker that we've modified the cart
with BatchController.batch(self.cart.user):
# Mark the version of self in the batch cache as modified
memoised = self.for_user(self.cart.user)
memoised._modified_by_batch = True
return func(self, *a, **k)
return inner
@ -47,6 +49,7 @@ class CartController(object):
self.cart = cart
@classmethod
@BatchController.memoise
def for_user(cls, user):
''' Returns the user's current cart, or creates a new cart
if there isn't one ready yet. '''
@ -64,59 +67,6 @@ class CartController(object):
)
return cls(existing)
# Marks the carts that are currently in batches
_FOR_USER = {}
_BATCH_COUNT = collections.defaultdict(int)
_MODIFIED_CARTS = set()
class _ModificationMarker(object):
pass
@classmethod
@contextlib.contextmanager
def operations_batch(cls, user):
''' Marks the boundary for a batch of operations on a user's cart.
These markers can be nested. Only on exiting the outermost marker will
a batch be ended.
When a batch is ended, discounts are recalculated, and the cart's
revision is increased.
'''
if user not in cls._FOR_USER:
_ctrl = cls.for_user(user)
cls._FOR_USER[user] = (_ctrl, _ctrl.cart.id)
ctrl, _id = cls._FOR_USER[user]
cls._BATCH_COUNT[_id] += 1
try:
success = False
marker = cls._ModificationMarker()
yield marker
if hasattr(marker, "mark"):
cls._MODIFIED_CARTS.add(_id)
success = True
finally:
cls._BATCH_COUNT[_id] -= 1
# Only end on the outermost batch marker, and only if
# it excited cleanly, and a modification occurred
modified = _id in cls._MODIFIED_CARTS
outermost = cls._BATCH_COUNT[_id] == 0
if modified and outermost and success:
ctrl._end_batch()
cls._MODIFIED_CARTS.remove(_id)
# Clear out the cache on the outermost operation
if outermost:
del cls._FOR_USER[user]
def _fail_if_cart_is_not_active(self):
self.cart.refresh_from_db()
if self.cart.status != commerce.Cart.STATUS_ACTIVE:
@ -144,6 +94,13 @@ class CartController(object):
self.cart.time_last_updated = timezone.now()
self.cart.reservation_duration = max(reservations)
def end_batch(self):
''' Calls ``_end_batch`` if a modification has been performed in the
previous batch. '''
if hasattr(self,'_modified_by_batch'):
self._end_batch()
def _end_batch(self):
''' Performs operations that occur occur at the end of a batch of
product changes/voucher applications etc.