CartController now uses BatchController memoisation
This commit is contained in:
		
							parent
							
								
									3717adb262
								
							
						
					
					
						commit
						3d635521eb
					
				
					 1 changed files with 13 additions and 56 deletions
				
			
		|  | @ -16,6 +16,7 @@ from registrasion.models import commerce | ||||||
| from registrasion.models import conditions | from registrasion.models import conditions | ||||||
| from registrasion.models import inventory | from registrasion.models import inventory | ||||||
| 
 | 
 | ||||||
|  | from.batch import BatchController | ||||||
| from .category import CategoryController | from .category import CategoryController | ||||||
| from .discount import DiscountController | from .discount import DiscountController | ||||||
| from .flag import FlagController | from .flag import FlagController | ||||||
|  | @ -34,10 +35,11 @@ def _modifies_cart(func): | ||||||
|     def inner(self, *a, **k): |     def inner(self, *a, **k): | ||||||
|         self._fail_if_cart_is_not_active() |         self._fail_if_cart_is_not_active() | ||||||
|         with transaction.atomic(): |         with transaction.atomic(): | ||||||
|             with CartController.operations_batch(self.cart.user) as mark: |             with BatchController.batch(self.cart.user): | ||||||
|                 mark.mark = True  # Marker that we've modified the cart |                 # 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 func(self, *a, **k) | ||||||
| 
 |  | ||||||
|     return inner |     return inner | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -47,6 +49,7 @@ class CartController(object): | ||||||
|         self.cart = cart |         self.cart = cart | ||||||
| 
 | 
 | ||||||
|     @classmethod |     @classmethod | ||||||
|  |     @BatchController.memoise | ||||||
|     def for_user(cls, user): |     def for_user(cls, user): | ||||||
|         ''' Returns the user's current cart, or creates a new cart |         ''' Returns the user's current cart, or creates a new cart | ||||||
|         if there isn't one ready yet. ''' |         if there isn't one ready yet. ''' | ||||||
|  | @ -64,59 +67,6 @@ class CartController(object): | ||||||
|             ) |             ) | ||||||
|         return cls(existing) |         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): |     def _fail_if_cart_is_not_active(self): | ||||||
|         self.cart.refresh_from_db() |         self.cart.refresh_from_db() | ||||||
|         if self.cart.status != commerce.Cart.STATUS_ACTIVE: |         if self.cart.status != commerce.Cart.STATUS_ACTIVE: | ||||||
|  | @ -144,6 +94,13 @@ class CartController(object): | ||||||
|         self.cart.time_last_updated = timezone.now() |         self.cart.time_last_updated = timezone.now() | ||||||
|         self.cart.reservation_duration = max(reservations) |         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): |     def _end_batch(self): | ||||||
|         ''' Performs operations that occur occur at the end of a batch of |         ''' Performs operations that occur occur at the end of a batch of | ||||||
|         product changes/voucher applications etc. |         product changes/voucher applications etc. | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 Christopher Neugebauer
						Christopher Neugebauer