Used conditional expressions for quota calculation

This commit is contained in:
Raphael Michel
2015-10-19 20:04:43 +02:00
parent 1dfdf7e4f4
commit 6678539aa8

View File

@@ -1,7 +1,8 @@
from itertools import product from itertools import product
from django.db import models from django.db import models
from django.db.models import Q, Count from django.db.models import Q, Case, Count, Sum, When
from django.utils.functional import cached_property
from django.utils.timezone import now from django.utils.timezone import now
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from versions.models import VersionedForeignKey, VersionedManyToManyField from versions.models import VersionedForeignKey, VersionedManyToManyField
@@ -808,15 +809,55 @@ class Quota(Versionable):
:returns: a tuple where the first entry is one of the ``Quota.AVAILABILITY_`` constants :returns: a tuple where the first entry is one of the ``Quota.AVAILABILITY_`` constants
and the second is the number of available tickets. and the second is the number of available tickets.
""" """
from pretix.base.models import Order, OrderPosition, CartPosition size_left = self.size
# TODO: These lookups are highly inefficient. However, we'll wait with optimizing
# until Django 1.8 is released, as the following feature might make it a
# lot easier:
# https://docs.djangoproject.com/en/1.8/ref/models/conditional-expressions/
# TODO: Test for interference with old versions of Item-Quota-relations, etc. # TODO: Test for interference with old versions of Item-Quota-relations, etc.
# TODO: Prevent corner-cases like people having ordered an item before it got # TODO: Prevent corner-cases like people having ordered an item before it got
# its first variationsadded # its first variationsadde
quotalookup = ( orders = self.count_orders()
size_left -= orders['paid']
if size_left <= 0:
return Quota.AVAILABILITY_GONE, 0
size_left -= orders['pending']
if size_left <= 0:
return Quota.AVAILABILITY_ORDERED, 0
size_left -= self.count_in_cart()
if size_left <= 0:
return Quota.AVAILABILITY_RESERVED, 0
return Quota.AVAILABILITY_OK, size_left
def count_in_cart(self) -> int:
from pretix.base.models import CartPosition
return CartPosition.objects.current.filter(
Q(expires__gte=now())
& self._position_lookup
).count()
def count_orders(self) -> dict:
from pretix.base.models import Order, OrderPosition
o = OrderPosition.objects.current.filter(self._position_lookup).aggregate(
paid=Sum(
Case(When(order__status=Order.STATUS_PAID, then=1),
output_field=models.IntegerField())
),
pending=Sum(
Case(When(Q(order__status=Order.STATUS_PENDING) & Q(order__expires__gte=now()), then=1),
output_field=models.IntegerField())
)
)
for k, v in o.items():
if v is None:
o[k] = 0
return o
@cached_property
def _position_lookup(self):
return (
( # Orders for items which do not have any variations ( # Orders for items which do not have any variations
Q(variation__isnull=True) Q(variation__isnull=True)
& Q(item__quotas__in=[self]) & Q(item__quotas__in=[self])
@@ -825,30 +866,5 @@ class Quota(Versionable):
) )
) )
paid_orders = OrderPosition.objects.current.filter(
Q(order__status=Order.STATUS_PAID)
& quotalookup
).count()
if paid_orders >= self.size:
return Quota.AVAILABILITY_GONE, 0
pending_valid_orders = OrderPosition.objects.current.filter(
Q(order__status=Order.STATUS_PENDING)
& Q(order__expires__gte=now())
& quotalookup
).count()
if (paid_orders + pending_valid_orders) >= self.size:
return Quota.AVAILABILITY_ORDERED, 0
valid_cart_positions = CartPosition.objects.current.filter(
Q(expires__gte=now())
& quotalookup
).count()
if (paid_orders + pending_valid_orders + valid_cart_positions) >= self.size:
return Quota.AVAILABILITY_RESERVED, 0
return Quota.AVAILABILITY_OK, self.size - paid_orders - pending_valid_orders - valid_cart_positions
class QuotaExceededException(Exception): class QuotaExceededException(Exception):
pass pass