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