More low-hanging query optimisations
This commit is contained in:
		
							parent
							
								
									4fb569d935
								
							
						
					
					
						commit
						6d52a4c18f
					
				
					 4 changed files with 58 additions and 43 deletions
				
			
		|  | @ -8,6 +8,7 @@ from django.core.exceptions import ObjectDoesNotExist | |||
| from django.core.exceptions import ValidationError | ||||
| from django.db import transaction | ||||
| from django.db.models import Max | ||||
| from django.db.models import Q | ||||
| from django.utils import timezone | ||||
| 
 | ||||
| from registrasion.exceptions import CartValidationError | ||||
|  | @ -84,6 +85,8 @@ class CartController(object): | |||
|         revision is increased. | ||||
|         ''' | ||||
| 
 | ||||
|         # TODO cache carts mid-batch? | ||||
| 
 | ||||
|         ctrl = cls.for_user(user) | ||||
|         _id = ctrl.cart.id | ||||
| 
 | ||||
|  | @ -180,22 +183,28 @@ class CartController(object): | |||
|         # Validate that the limits we're adding are OK | ||||
|         self._test_limits(all_product_quantities) | ||||
| 
 | ||||
|         new_items = [] | ||||
|         products = [] | ||||
|         for product, quantity in product_quantities: | ||||
|             try: | ||||
|                 product_item = commerce.ProductItem.objects.get( | ||||
|                     cart=self.cart, | ||||
|                     product=product, | ||||
|                 ) | ||||
|                 product_item.quantity = quantity | ||||
|                 product_item.save() | ||||
|             except ObjectDoesNotExist: | ||||
|                 commerce.ProductItem.objects.create( | ||||
|                     cart=self.cart, | ||||
|                     product=product, | ||||
|                     quantity=quantity, | ||||
|                 ) | ||||
|             products.append(product) | ||||
| 
 | ||||
|         items_in_cart.filter(quantity=0).delete() | ||||
|             if quantity == 0: | ||||
|                 continue | ||||
| 
 | ||||
|             item = commerce.ProductItem( | ||||
|                 cart=self.cart, | ||||
|                 product=product, | ||||
|                 quantity=quantity, | ||||
|             ) | ||||
|             new_items.append(item) | ||||
| 
 | ||||
|         to_delete = ( | ||||
|             Q(quantity=0) | | ||||
|             Q(product__in=products) | ||||
|         ) | ||||
| 
 | ||||
|         items_in_cart.filter(to_delete).delete() | ||||
|         commerce.ProductItem.objects.bulk_create(new_items) | ||||
| 
 | ||||
|     def _test_limits(self, product_quantities): | ||||
|         ''' Tests that the quantity changes we intend to make do not violate | ||||
|  |  | |||
|  | @ -154,12 +154,17 @@ class CategoryConditionController(IsMetByFilter, ConditionController): | |||
|         product from a category invoking that item's condition in one of their | ||||
|         carts. ''' | ||||
| 
 | ||||
|         items = commerce.ProductItem.objects.filter(cart__user=user) | ||||
|         items = items.exclude(cart__status=commerce.Cart.STATUS_RELEASED) | ||||
|         items = items.select_related("product", "product__category") | ||||
|         categories = [item.product.category for item in items] | ||||
|         in_user_carts = Q( | ||||
|             enabling_category__product__productitem__cart__user=user | ||||
|         ) | ||||
|         released = commerce.Cart.STATUS_RELEASED | ||||
|         in_released_carts = Q( | ||||
|             enabling_category__product__productitem__cart__status=released | ||||
|         ) | ||||
|         queryset = queryset.filter(in_user_carts) | ||||
|         queryset = queryset.exclude(in_released_carts) | ||||
| 
 | ||||
|         return queryset.filter(enabling_category__in=categories) | ||||
|         return queryset | ||||
| 
 | ||||
| 
 | ||||
| class ProductConditionController(IsMetByFilter, ConditionController): | ||||
|  | @ -171,12 +176,15 @@ class ProductConditionController(IsMetByFilter, ConditionController): | |||
|         ''' Returns all of the items from queryset where the user has a | ||||
|         product invoking that item's condition in one of their carts. ''' | ||||
| 
 | ||||
|         items = commerce.ProductItem.objects.filter(cart__user=user) | ||||
|         items = items.exclude(cart__status=commerce.Cart.STATUS_RELEASED) | ||||
|         items = items.select_related("product", "product__category") | ||||
|         products = [item.product for item in items] | ||||
|         in_user_carts = Q(enabling_products__productitem__cart__user=user) | ||||
|         released = commerce.Cart.STATUS_RELEASED | ||||
|         in_released_carts = Q( | ||||
|             enabling_products__productitem__cart__status=released | ||||
|         ) | ||||
|         queryset = queryset.filter(in_user_carts) | ||||
|         queryset = queryset.exclude(in_released_carts) | ||||
| 
 | ||||
|         return queryset.filter(enabling_products__in=products) | ||||
|         return queryset | ||||
| 
 | ||||
| 
 | ||||
| class TimeOrStockLimitConditionController( | ||||
|  | @ -287,9 +295,4 @@ class VoucherConditionController(IsMetByFilter, ConditionController): | |||
|         ''' Returns all of the items from queryset where the user has entered | ||||
|         a voucher that invokes that item's condition in one of their carts. ''' | ||||
| 
 | ||||
|         carts = commerce.Cart.objects.filter( | ||||
|             user=user, | ||||
|         ) | ||||
|         vouchers = [cart.vouchers.all() for cart in carts] | ||||
| 
 | ||||
|         return queryset.filter(voucher__in=itertools.chain(*vouchers)) | ||||
|         return queryset.filter(voucher__cart__user=user) | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ import operator | |||
| from collections import defaultdict | ||||
| from collections import namedtuple | ||||
| from django.db.models import Count | ||||
| from django.db.models import Q | ||||
| 
 | ||||
| from .conditions import ConditionController | ||||
| 
 | ||||
|  | @ -83,18 +84,16 @@ class FlagController(object): | |||
| 
 | ||||
|             # Get all products covered by this condition, and the products | ||||
|             # from the categories covered by this condition | ||||
|             cond_products = condition.products.all() | ||||
|             from_category = inventory.Product.objects.filter( | ||||
|                 category__in=condition.categories.all(), | ||||
|             ).all() | ||||
|             all_products = cond_products | from_category | ||||
| 
 | ||||
|             ids = [product.id for product in products] | ||||
|             all_products = inventory.Product.objects.filter(id__in=ids) | ||||
|             cond = ( | ||||
|                 Q(flagbase_set=condition) | | ||||
|                 Q(category__in=condition.categories.all()) | ||||
|             ) | ||||
| 
 | ||||
|             all_products = all_products.filter(cond) | ||||
|             all_products = all_products.select_related("category") | ||||
|             # Remove the products that we aren't asking about | ||||
|             all_products = [ | ||||
|                 product | ||||
|                 for product in all_products | ||||
|                 if product in products | ||||
|             ] | ||||
| 
 | ||||
|             if quantities: | ||||
|                 consumed = sum(quantities[i] for i in all_products) | ||||
|  | @ -221,6 +220,7 @@ _ConditionsCount = namedtuple( | |||
| ) | ||||
| 
 | ||||
| 
 | ||||
| # TODO: this should be cacheable. | ||||
| class FlagCounter(_FlagCounter): | ||||
| 
 | ||||
|     @classmethod | ||||
|  |  | |||
|  | @ -445,14 +445,17 @@ def _handle_products(request, category, products, prefix): | |||
| def _set_quantities_from_products_form(products_form, current_cart): | ||||
| 
 | ||||
|     quantities = list(products_form.product_quantities()) | ||||
| 
 | ||||
|     id_to_quantity = dict(i[:2] for i in quantities) | ||||
|     pks = [i[0] for i in quantities] | ||||
|     products = inventory.Product.objects.filter( | ||||
|         id__in=pks, | ||||
|     ).select_related("category") | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     # TODO: This is fundamentally dumb | ||||
|     product_quantities = [ | ||||
|         (products.get(pk=i[0]), i[1]) for i in quantities | ||||
|         (product, id_to_quantity[product.id]) for product in products | ||||
|     ] | ||||
|     field_names = dict( | ||||
|         (i[0][0], i[1][2]) for i in zip(product_quantities, quantities) | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 Christopher Neugebauer
						Christopher Neugebauer