Optimises queries through simplifying repeated queries and select_related use
This commit is contained in:
parent
5debbb2ac8
commit
53413388e0
7 changed files with 95 additions and 38 deletions
|
@ -80,6 +80,11 @@ class CartController(object):
|
||||||
pairs. '''
|
pairs. '''
|
||||||
|
|
||||||
items_in_cart = rego.ProductItem.objects.filter(cart=self.cart)
|
items_in_cart = rego.ProductItem.objects.filter(cart=self.cart)
|
||||||
|
items_in_cart = items_in_cart.select_related(
|
||||||
|
"product",
|
||||||
|
"product__category",
|
||||||
|
)
|
||||||
|
|
||||||
product_quantities = list(product_quantities)
|
product_quantities = list(product_quantities)
|
||||||
|
|
||||||
# n.b need to add have the existing items first so that the new
|
# n.b need to add have the existing items first so that the new
|
||||||
|
@ -283,6 +288,7 @@ class CartController(object):
|
||||||
|
|
||||||
# Fix products and discounts
|
# Fix products and discounts
|
||||||
items = rego.ProductItem.objects.filter(cart=self.cart)
|
items = rego.ProductItem.objects.filter(cart=self.cart)
|
||||||
|
items = items.select_related("product")
|
||||||
products = set(i.product for i in items)
|
products = set(i.product for i in items)
|
||||||
available = set(ProductController.available_products(
|
available = set(ProductController.available_products(
|
||||||
self.cart.user,
|
self.cart.user,
|
||||||
|
@ -302,7 +308,9 @@ class CartController(object):
|
||||||
# Delete the existing entries.
|
# Delete the existing entries.
|
||||||
rego.DiscountItem.objects.filter(cart=self.cart).delete()
|
rego.DiscountItem.objects.filter(cart=self.cart).delete()
|
||||||
|
|
||||||
product_items = self.cart.productitem_set.all()
|
product_items = self.cart.productitem_set.all().select_related(
|
||||||
|
"product", "product__category",
|
||||||
|
)
|
||||||
|
|
||||||
products = [i.product for i in product_items]
|
products = [i.product for i in product_items]
|
||||||
discounts = discount.available_discounts(self.cart.user, [], products)
|
discounts = discount.available_discounts(self.cart.user, [], products)
|
||||||
|
@ -310,6 +318,7 @@ class CartController(object):
|
||||||
# The highest-value discounts will apply to the highest-value
|
# The highest-value discounts will apply to the highest-value
|
||||||
# products first.
|
# products first.
|
||||||
product_items = self.cart.productitem_set.all()
|
product_items = self.cart.productitem_set.all()
|
||||||
|
product_items = product_items.select_related("product")
|
||||||
product_items = product_items.order_by('product__price')
|
product_items = product_items.order_by('product__price')
|
||||||
product_items = reversed(product_items)
|
product_items = reversed(product_items)
|
||||||
for item in product_items:
|
for item in product_items:
|
||||||
|
|
|
@ -22,7 +22,7 @@ class CategoryController(object):
|
||||||
from product import ProductController
|
from product import ProductController
|
||||||
|
|
||||||
if products is AllProducts:
|
if products is AllProducts:
|
||||||
products = rego.Product.objects.all()
|
products = rego.Product.objects.all().select_related("category")
|
||||||
|
|
||||||
available = ProductController.available_products(
|
available = ProductController.available_products(
|
||||||
user,
|
user,
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import itertools
|
import itertools
|
||||||
|
import operator
|
||||||
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
|
@ -90,12 +91,22 @@ class ConditionController(object):
|
||||||
quantities = {}
|
quantities = {}
|
||||||
|
|
||||||
# Get the conditions covered by the products themselves
|
# Get the conditions covered by the products themselves
|
||||||
all_conditions = [
|
|
||||||
product.enablingconditionbase_set.select_subclasses() |
|
prods = (
|
||||||
product.category.enablingconditionbase_set.select_subclasses()
|
product.enablingconditionbase_set.select_subclasses()
|
||||||
for product in products
|
for product in products
|
||||||
]
|
)
|
||||||
all_conditions = set(itertools.chain(*all_conditions))
|
# Get the conditions covered by their categories
|
||||||
|
cats = (
|
||||||
|
category.enablingconditionbase_set.select_subclasses()
|
||||||
|
for category in set(product.category for product in products)
|
||||||
|
)
|
||||||
|
|
||||||
|
if products:
|
||||||
|
# Simplify the query.
|
||||||
|
all_conditions = reduce(operator.or_, itertools.chain(prods, cats))
|
||||||
|
else:
|
||||||
|
all_conditions = []
|
||||||
|
|
||||||
# All mandatory conditions on a product need to be met
|
# All mandatory conditions on a product need to be met
|
||||||
mandatory = defaultdict(lambda: True)
|
mandatory = defaultdict(lambda: True)
|
||||||
|
@ -115,10 +126,14 @@ class ConditionController(object):
|
||||||
from_category = rego.Product.objects.filter(
|
from_category = rego.Product.objects.filter(
|
||||||
category__in=condition.categories.all(),
|
category__in=condition.categories.all(),
|
||||||
).all()
|
).all()
|
||||||
all_products = set(itertools.chain(cond_products, from_category))
|
all_products = cond_products | from_category
|
||||||
|
all_products = all_products.select_related("category")
|
||||||
# Remove the products that we aren't asking about
|
# Remove the products that we aren't asking about
|
||||||
all_products = all_products & products
|
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)
|
||||||
|
|
|
@ -13,7 +13,7 @@ class DiscountAndQuantity(object):
|
||||||
self.quantity = quantity
|
self.quantity = quantity
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
print "(discount=%s, clause=%s, quantity=%d)" % (
|
return "(discount=%s, clause=%s, quantity=%d)" % (
|
||||||
self.discount, self.clause, self.quantity,
|
self.discount, self.clause, self.quantity,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -37,11 +37,20 @@ def available_discounts(user, categories, products):
|
||||||
)
|
)
|
||||||
# (Not relevant: discounts that match products in provided categories)
|
# (Not relevant: discounts that match products in provided categories)
|
||||||
|
|
||||||
|
product_discounts = product_discounts.select_related(
|
||||||
|
"product",
|
||||||
|
"product__category",
|
||||||
|
)
|
||||||
|
|
||||||
|
all_category_discounts = category_discounts | product_category_discounts
|
||||||
|
all_category_discounts = all_category_discounts.select_related(
|
||||||
|
"category",
|
||||||
|
)
|
||||||
|
|
||||||
# The set of all potential discounts
|
# The set of all potential discounts
|
||||||
potential_discounts = set(itertools.chain(
|
potential_discounts = set(itertools.chain(
|
||||||
product_discounts,
|
product_discounts,
|
||||||
category_discounts,
|
all_category_discounts,
|
||||||
product_category_discounts,
|
|
||||||
))
|
))
|
||||||
|
|
||||||
discounts = []
|
discounts = []
|
||||||
|
@ -50,6 +59,7 @@ def available_discounts(user, categories, products):
|
||||||
accepted_discounts = set()
|
accepted_discounts = set()
|
||||||
failed_discounts = set()
|
failed_discounts = set()
|
||||||
|
|
||||||
|
|
||||||
for discount in potential_discounts:
|
for discount in potential_discounts:
|
||||||
real_discount = rego.DiscountBase.objects.get_subclass(
|
real_discount = rego.DiscountBase.objects.get_subclass(
|
||||||
pk=discount.discount.pk,
|
pk=discount.discount.pk,
|
||||||
|
@ -63,7 +73,7 @@ def available_discounts(user, categories, products):
|
||||||
cart__user=user,
|
cart__user=user,
|
||||||
cart__active=False, # Only past carts count
|
cart__active=False, # Only past carts count
|
||||||
cart__released=False, # You can reuse refunded discounts
|
cart__released=False, # You can reuse refunded discounts
|
||||||
discount=discount.discount,
|
discount=real_discount,
|
||||||
)
|
)
|
||||||
agg = past_uses.aggregate(Sum("quantity"))
|
agg = past_uses.aggregate(Sum("quantity"))
|
||||||
past_use_count = agg["quantity__sum"]
|
past_use_count = agg["quantity__sum"]
|
||||||
|
|
|
@ -23,18 +23,25 @@ class ProductController(object):
|
||||||
|
|
||||||
if category is not None:
|
if category is not None:
|
||||||
all_products = rego.Product.objects.filter(category=category)
|
all_products = rego.Product.objects.filter(category=category)
|
||||||
|
all_products = all_products.select_related("category")
|
||||||
else:
|
else:
|
||||||
all_products = []
|
all_products = []
|
||||||
|
|
||||||
if products is not None:
|
if products is not None:
|
||||||
all_products = itertools.chain(all_products, products)
|
all_products = set(itertools.chain(all_products, products))
|
||||||
|
|
||||||
|
cat_quants = dict(
|
||||||
|
(
|
||||||
|
category,
|
||||||
|
CategoryController(category).user_quantity_remaining(user),
|
||||||
|
)
|
||||||
|
for category in set(product.category for product in all_products)
|
||||||
|
)
|
||||||
|
|
||||||
passed_limits = set(
|
passed_limits = set(
|
||||||
product
|
product
|
||||||
for product in all_products
|
for product in all_products
|
||||||
if CategoryController(product.category).user_quantity_remaining(
|
if cat_quants[product.category] > 0
|
||||||
user
|
|
||||||
) > 0
|
|
||||||
if cls(product).user_quantity_remaining(user) > 0
|
if cls(product).user_quantity_remaining(user) > 0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ def items_pending(context):
|
||||||
all_items = rego.ProductItem.objects.filter(
|
all_items = rego.ProductItem.objects.filter(
|
||||||
cart__user=context.request.user,
|
cart__user=context.request.user,
|
||||||
cart__active=True,
|
cart__active=True,
|
||||||
)
|
).select_related("product", "product__category")
|
||||||
return all_items
|
return all_items
|
||||||
|
|
||||||
|
|
||||||
|
@ -42,17 +42,18 @@ def items_purchased(context, category=None):
|
||||||
all_items = rego.ProductItem.objects.filter(
|
all_items = rego.ProductItem.objects.filter(
|
||||||
cart__user=context.request.user,
|
cart__user=context.request.user,
|
||||||
cart__active=False,
|
cart__active=False,
|
||||||
)
|
cart__released=False,
|
||||||
|
).select_related("product", "product__category")
|
||||||
|
|
||||||
if category:
|
if category:
|
||||||
all_items = all_items.filter(product__category=category)
|
all_items = all_items.filter(product__category=category)
|
||||||
|
|
||||||
products = set(item.product for item in all_items)
|
pq = all_items.values("product").annotate(quantity=Sum("quantity")).all()
|
||||||
|
products = rego.Product.objects.all()
|
||||||
out = []
|
out = []
|
||||||
for product in products:
|
for item in pq:
|
||||||
pp = all_items.filter(product=product)
|
prod = products.get(pk=item["product"])
|
||||||
quantity = pp.aggregate(Sum("quantity"))["quantity__sum"]
|
out.append(ProductAndQuantity(prod, item["quantity"]))
|
||||||
out.append(ProductAndQuantity(product, quantity))
|
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -115,11 +115,20 @@ def guided_registration(request, page_id=0):
|
||||||
current_step = 3
|
current_step = 3
|
||||||
title = "Additional items"
|
title = "Additional items"
|
||||||
|
|
||||||
for category in cats:
|
all_products = rego.Product.objects.filter(
|
||||||
products = ProductController.available_products(
|
category__in=cats,
|
||||||
|
).select_related("category")
|
||||||
|
|
||||||
|
available_products = set(ProductController.available_products(
|
||||||
request.user,
|
request.user,
|
||||||
category=category,
|
products=all_products,
|
||||||
)
|
))
|
||||||
|
|
||||||
|
for category in cats:
|
||||||
|
products = [
|
||||||
|
i for i in available_products
|
||||||
|
if i.category == category
|
||||||
|
]
|
||||||
|
|
||||||
prefix = "category_" + str(category.id)
|
prefix = "category_" + str(category.id)
|
||||||
p = handle_products(request, category, products, prefix)
|
p = handle_products(request, category, products, prefix)
|
||||||
|
@ -280,15 +289,17 @@ def handle_products(request, category, products, prefix):
|
||||||
items = rego.ProductItem.objects.filter(
|
items = rego.ProductItem.objects.filter(
|
||||||
product__in=products,
|
product__in=products,
|
||||||
cart=current_cart.cart,
|
cart=current_cart.cart,
|
||||||
)
|
).select_related("product")
|
||||||
quantities = []
|
quantities = []
|
||||||
for product in products:
|
seen = set()
|
||||||
# Only add items that are enabled.
|
|
||||||
try:
|
for item in items:
|
||||||
quantity = items.get(product=product).quantity
|
quantities.append((item.product, item.quantity))
|
||||||
except ObjectDoesNotExist:
|
seen.add(item.product)
|
||||||
quantity = 0
|
|
||||||
quantities.append((product, quantity))
|
zeros = set(products) - seen
|
||||||
|
for product in zeros:
|
||||||
|
quantities.append((product, 0))
|
||||||
|
|
||||||
products_form = ProductsForm(
|
products_form = ProductsForm(
|
||||||
request.POST or None,
|
request.POST or None,
|
||||||
|
@ -323,8 +334,12 @@ 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())
|
||||||
|
|
||||||
|
pks = [i[0] for i in quantities]
|
||||||
|
products = rego.Product.objects.filter(id__in=pks).select_related("category")
|
||||||
|
|
||||||
product_quantities = [
|
product_quantities = [
|
||||||
(rego.Product.objects.get(pk=i[0]), i[1]) for i in quantities
|
(products.get(pk=i[0]), i[1]) for i in quantities
|
||||||
]
|
]
|
||||||
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