Puts attach_remainders on ProductController and CategoryController, eliminating the need to query each product and category separately.
This commit is contained in:
parent
3b5b958b78
commit
a79ad3520e
3 changed files with 113 additions and 53 deletions
|
@ -203,13 +203,17 @@ class CartController(object):
|
|||
|
||||
errors = []
|
||||
|
||||
# Pre-annotate products
|
||||
products = [p for (p, q) in product_quantities]
|
||||
r = ProductController.attach_user_remainders(self.cart.user, products)
|
||||
with_remainders = dict((p, p) for p in r)
|
||||
|
||||
# Test each product limit here
|
||||
for product, quantity in product_quantities:
|
||||
if quantity < 0:
|
||||
errors.append((product, "Value must be zero or greater."))
|
||||
|
||||
prod = ProductController(product)
|
||||
limit = prod.user_quantity_remaining(self.cart.user)
|
||||
limit = with_remainders[product].remainder
|
||||
|
||||
if quantity > limit:
|
||||
errors.append((
|
||||
|
@ -224,10 +228,15 @@ class CartController(object):
|
|||
for product, quantity in product_quantities:
|
||||
by_cat[product.category].append((product, quantity))
|
||||
|
||||
# Pre-annotate categories
|
||||
r = CategoryController.attach_user_remainders(self.cart.user, by_cat)
|
||||
with_remainders = dict((cat, cat) for cat in r)
|
||||
|
||||
# Test each category limit here
|
||||
for category in by_cat:
|
||||
ctrl = CategoryController(category)
|
||||
limit = ctrl.user_quantity_remaining(self.cart.user)
|
||||
#ctrl = CategoryController(category)
|
||||
#limit = ctrl.user_quantity_remaining(self.cart.user)
|
||||
limit = with_remainders[category].remainder
|
||||
|
||||
# Get the amount so far in the cart
|
||||
to_add = sum(i[1] for i in by_cat[category])
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
from registrasion.models import commerce
|
||||
from registrasion.models import inventory
|
||||
|
||||
from django.db.models import Case
|
||||
from django.db.models import F, Q
|
||||
from django.db.models import Sum
|
||||
from django.db.models import When
|
||||
from django.db.models import Value
|
||||
|
||||
|
||||
class AllProducts(object):
|
||||
|
@ -34,25 +38,48 @@ class CategoryController(object):
|
|||
|
||||
return set(i.category for i in available)
|
||||
|
||||
|
||||
@classmethod
|
||||
def attach_user_remainders(cls, user, categories):
|
||||
'''
|
||||
|
||||
Return:
|
||||
queryset(inventory.Product): A queryset containing items from
|
||||
``categories``, with an extra attribute -- remainder = the amount
|
||||
of items from this category that is remaining.
|
||||
'''
|
||||
|
||||
ids = [category.id for category in categories]
|
||||
categories = inventory.Category.objects.filter(id__in=ids)
|
||||
|
||||
cart_filter = (
|
||||
Q(product__productitem__cart__user=user) &
|
||||
Q(product__productitem__cart__status=commerce.Cart.STATUS_PAID)
|
||||
)
|
||||
|
||||
quantity = When(
|
||||
cart_filter,
|
||||
then='product__productitem__quantity'
|
||||
)
|
||||
|
||||
quantity_or_zero = Case(
|
||||
quantity,
|
||||
default=Value(0),
|
||||
)
|
||||
|
||||
remainder = Case(
|
||||
When(limit_per_user=None, then=Value(99999999)),
|
||||
default=F('limit_per_user') - Sum(quantity_or_zero),
|
||||
)
|
||||
|
||||
categories = categories.annotate(remainder=remainder)
|
||||
|
||||
return categories
|
||||
|
||||
def user_quantity_remaining(self, user):
|
||||
''' Returns the number of items from this category that the user may
|
||||
add in the current cart. '''
|
||||
''' Returns the quantity of this product that the user add in the
|
||||
current cart. '''
|
||||
|
||||
cat_limit = self.category.limit_per_user
|
||||
with_remainders = self.attach_user_remainders(user, [self.category])
|
||||
|
||||
if cat_limit is None:
|
||||
# We don't need to waste the following queries
|
||||
return 99999999
|
||||
|
||||
carts = commerce.Cart.objects.filter(
|
||||
user=user,
|
||||
status=commerce.Cart.STATUS_PAID,
|
||||
)
|
||||
|
||||
items = commerce.ProductItem.objects.filter(
|
||||
cart__in=carts,
|
||||
product__category=self.category,
|
||||
)
|
||||
|
||||
cat_count = items.aggregate(Sum("quantity"))["quantity__sum"] or 0
|
||||
return cat_limit - cat_count
|
||||
return with_remainders[0].remainder
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
import itertools
|
||||
|
||||
from django.db.models import Case
|
||||
from django.db.models import F, Q
|
||||
from django.db.models import Sum
|
||||
from django.db.models import When
|
||||
from django.db.models import Value
|
||||
|
||||
from registrasion.models import commerce
|
||||
from registrasion.models import inventory
|
||||
|
||||
|
@ -16,9 +21,7 @@ class ProductController(object):
|
|||
@classmethod
|
||||
def available_products(cls, user, category=None, products=None):
|
||||
''' Returns a list of all of the products that are available per
|
||||
flag conditions from the given categories.
|
||||
TODO: refactor so that all conditions are tested here and
|
||||
can_add_with_flags calls this method. '''
|
||||
flag conditions from the given categories. '''
|
||||
if category is None and products is None:
|
||||
raise ValueError("You must provide products or a category")
|
||||
|
||||
|
@ -31,19 +34,18 @@ class ProductController(object):
|
|||
if products is not None:
|
||||
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)
|
||||
)
|
||||
categories = set(product.category for product in all_products)
|
||||
r = CategoryController.attach_user_remainders(user, categories)
|
||||
cat_quants = dict((c,c) for c in r)
|
||||
|
||||
r = ProductController.attach_user_remainders(user, all_products)
|
||||
prod_quants = dict((p,p) for p in r)
|
||||
|
||||
passed_limits = set(
|
||||
product
|
||||
for product in all_products
|
||||
if cat_quants[product.category] > 0
|
||||
if cls(product).user_quantity_remaining(user) > 0
|
||||
if cat_quants[product.category].remainder > 0
|
||||
if prod_quants[product].remainder > 0
|
||||
)
|
||||
|
||||
failed_and_messages = FlagController.test_flags(
|
||||
|
@ -56,26 +58,48 @@ class ProductController(object):
|
|||
|
||||
return out
|
||||
|
||||
|
||||
@classmethod
|
||||
def attach_user_remainders(cls, user, products):
|
||||
'''
|
||||
|
||||
Return:
|
||||
queryset(inventory.Product): A queryset containing items from
|
||||
``product``, with an extra attribute -- remainder = the amount of
|
||||
this item that is remaining.
|
||||
'''
|
||||
|
||||
ids = [product.id for product in products]
|
||||
products = inventory.Product.objects.filter(id__in=ids)
|
||||
|
||||
cart_filter = (
|
||||
Q(productitem__cart__user=user) &
|
||||
Q(productitem__cart__status=commerce.Cart.STATUS_PAID)
|
||||
)
|
||||
|
||||
quantity = When(
|
||||
cart_filter,
|
||||
then='productitem__quantity'
|
||||
)
|
||||
|
||||
quantity_or_zero = Case(
|
||||
quantity,
|
||||
default=Value(0),
|
||||
)
|
||||
|
||||
remainder = Case(
|
||||
When(limit_per_user=None, then=Value(99999999)),
|
||||
default=F('limit_per_user') - Sum(quantity_or_zero),
|
||||
)
|
||||
|
||||
products = products.annotate(remainder=remainder)
|
||||
|
||||
return products
|
||||
|
||||
def user_quantity_remaining(self, user):
|
||||
''' Returns the quantity of this product that the user add in the
|
||||
current cart. '''
|
||||
|
||||
prod_limit = self.product.limit_per_user
|
||||
with_remainders = self.attach_user_remainders(user, [self.product])
|
||||
|
||||
if prod_limit is None:
|
||||
# Don't need to run the remaining queries
|
||||
return 999999 # We can do better
|
||||
|
||||
carts = commerce.Cart.objects.filter(
|
||||
user=user,
|
||||
status=commerce.Cart.STATUS_PAID,
|
||||
)
|
||||
|
||||
items = commerce.ProductItem.objects.filter(
|
||||
cart__in=carts,
|
||||
product=self.product,
|
||||
)
|
||||
|
||||
prod_count = items.aggregate(Sum("quantity"))["quantity__sum"] or 0
|
||||
|
||||
return prod_limit - prod_count
|
||||
return with_remainders[0].remainder
|
||||
|
|
Loading…
Reference in a new issue