diff --git a/src/pretix/base/models/items.py b/src/pretix/base/models/items.py index cbc5ad57b9..a54ffdb9f7 100644 --- a/src/pretix/base/models/items.py +++ b/src/pretix/base/models/items.py @@ -230,7 +230,7 @@ class Item(LoggedModel): return False return True - def check_quotas(self, ignored_quotas=None): + def check_quotas(self, ignored_quotas=None, _cache=None): """ This method is used to determine whether this Item is currently available for sale. @@ -252,7 +252,7 @@ class Item(LoggedModel): if self.variations.count() > 0: # NOQA raise ValueError('Do not call this directly on items which have variations ' 'but call this on their ItemVariation objects') - return min([q.availability() for q in check_quotas], + return min([q.availability(_cache=_cache) for q in check_quotas], key=lambda s: (s[0], s[1] if s[1] is not None else sys.maxsize)) @cached_property @@ -314,7 +314,7 @@ class ItemVariation(models.Model): if self.item: self.item.event.get_cache().clear() - def check_quotas(self, ignored_quotas=None) -> Tuple[int, int]: + def check_quotas(self, ignored_quotas=None, _cache=None) -> Tuple[int, int]: """ This method is used to determine whether this ItemVariation is currently available for sale in terms of quotas. @@ -330,7 +330,7 @@ class ItemVariation(models.Model): check_quotas -= set(ignored_quotas) if not check_quotas: return Quota.AVAILABILITY_OK, sys.maxsize - return min([q.availability() for q in check_quotas], + return min([q.availability(_cache=_cache) for q in check_quotas], key=lambda s: (s[0], s[1] if s[1] is not None else sys.maxsize)) def __lt__(self, other): @@ -533,7 +533,7 @@ class Quota(LoggedModel): if self.event: self.event.get_cache().clear() - def availability(self, now_dt: datetime=None) -> Tuple[int, int]: + def availability(self, now_dt: datetime=None, _cache=None) -> Tuple[int, int]: """ This method is used to determine whether Items or ItemVariations belonging to this quota should currently be available for sale. @@ -541,6 +541,14 @@ class Quota(LoggedModel): :returns: a tuple where the first entry is one of the ``Quota.AVAILABILITY_`` constants and the second is the number of available tickets. """ + if _cache is not None and self.pk in _cache: + return _cache[self.pk] + res = self._availability(now_dt) + if _cache is not None: + _cache[self.pk] = res + return res + + def _availability(self, now_dt: datetime=None): now_dt = now_dt or now() size_left = self.size if size_left is None: diff --git a/src/pretix/presale/views/event.py b/src/pretix/presale/views/event.py index bacf0bb20b..5222abf494 100644 --- a/src/pretix/presale/views/event.py +++ b/src/pretix/presale/views/event.py @@ -44,9 +44,10 @@ def get_grouped_items(event): quotac__gt=0 ).order_by('category__position', 'category_id', 'position', 'name') display_add_to_cart = False + quota_cache = {} for item in items: if not item.has_variations: - item.cached_availability = list(item.check_quotas()) + item.cached_availability = list(item.check_quotas(_cache=quota_cache)) item.order_max = min(item.cached_availability[1] if item.cached_availability[1] is not None else sys.maxsize, int(event.settings.max_items_per_order)) @@ -54,7 +55,7 @@ def get_grouped_items(event): display_add_to_cart = display_add_to_cart or item.order_max > 0 else: for var in item.available_variations: - var.cached_availability = list(var.check_quotas()) + var.cached_availability = list(var.check_quotas(_cache=quota_cache)) var.order_max = min(var.cached_availability[1] if var.cached_availability[1] is not None else sys.maxsize, int(event.settings.max_items_per_order))