diff --git a/src/pretix/api/views/event.py b/src/pretix/api/views/event.py index 8ac48febb0..1195b9d991 100644 --- a/src/pretix/api/views/event.py +++ b/src/pretix/api/views/event.py @@ -41,6 +41,7 @@ from django_filters.rest_framework import DjangoFilterBackend, FilterSet from django_scopes import scopes_disabled from rest_framework import serializers, views, viewsets from rest_framework.exceptions import PermissionDenied, ValidationError +from rest_framework.generics import get_object_or_404 from rest_framework.response import Response from pretix.api.auth.permission import EventCRUDPermission @@ -162,7 +163,13 @@ class EventViewSet(viewsets.ModelViewSet): qs = filter_qs_by_attr(qs, self.request) if 'with_availability_for' in self.request.GET: - qs = Event.annotated(qs, channel=self.request.GET.get('with_availability_for')) + qs = Event.annotated( + qs, + channel=get_object_or_404( + self.request.organizer.sales_channels, + identifier=self.request.GET.get('with_availability_for') + ) + ) return qs.prefetch_related( 'organizer', @@ -442,7 +449,13 @@ class SubEventViewSet(ConditionalListView, viewsets.ModelViewSet): qs = filter_qs_by_attr(qs, self.request) if 'with_availability_for' in self.request.GET: - qs = SubEvent.annotated(qs, channel=self.request.GET.get('with_availability_for')) + qs = SubEvent.annotated( + qs, + channel=get_object_or_404( + self.request.organizer.sales_channels, + identifier=self.request.GET.get('with_availability_for') + ) + ) return qs.prefetch_related( 'event', diff --git a/src/pretix/base/models/event.py b/src/pretix/base/models/event.py index f65ee59c19..a9f4302a08 100644 --- a/src/pretix/base/models/event.py +++ b/src/pretix/base/models/event.py @@ -304,10 +304,13 @@ class EventMixin: return safe_string(json.dumps(eventdict)) @classmethod - def annotated(cls, qs, channel='web', voucher=None): - from pretix.base.models import Item, ItemVariation, Quota + def annotated(cls, qs, channel, voucher=None): + # Channel can currently be a SalesChannel or a str, since we need that compatibility, but a SalesChannel + # makes the query SIGNIFICANTLY faster + from pretix.base.models import Item, ItemVariation, Quota, SalesChannel + + assert isinstance(channel, (SalesChannel, str)) - assert isinstance(channel, str) sq_active_item = Item.objects.using(settings.DATABASE_REPLICA).filter_available(channel=channel, voucher=voucher).filter( Q(variations__isnull=True) & Q(quotas__pk=OuterRef('pk')) @@ -317,18 +320,23 @@ class EventMixin: q_variation = ( Q(active=True) - & Q(Q(all_sales_channels=True) | Q(limit_sales_channels__identifier=channel)) & Q(Q(available_from__isnull=True) | Q(available_from__lte=time_machine_now())) & Q(Q(available_until__isnull=True) | Q(available_until__gte=time_machine_now())) & Q(item__active=True) & Q(Q(item__available_from__isnull=True) | Q(item__available_from__lte=time_machine_now())) & Q(Q(item__available_until__isnull=True) | Q(item__available_until__gte=time_machine_now())) & Q(Q(item__category__isnull=True) | Q(item__category__is_addon=False)) - & Q(Q(item__all_sales_channels=True) | Q(item__limit_sales_channels__identifier=channel)) & Q(item__require_bundling=False) & Q(quotas__pk=OuterRef('pk')) ) + if isinstance(channel, str): + q_variation &= Q(Q(all_sales_channels=True) | Q(limit_sales_channels__identifier=channel)) + q_variation &= Q(Q(item__all_sales_channels=True) | Q(item__limit_sales_channels__identifier=channel)) + else: + q_variation &= Q(Q(all_sales_channels=True) | Q(limit_sales_channels=channel)) + q_variation &= Q(Q(item__all_sales_channels=True) | Q(item__limit_sales_channels=channel)) + if voucher: if voucher.variation_id: q_variation &= Q(pk=voucher.variation_id) @@ -1536,8 +1544,11 @@ class SubEvent(EventMixin, LoggedModel): return qs_annotated @classmethod - def annotated(cls, qs, channel='web', voucher=None): + def annotated(cls, qs, channel, voucher=None): from .items import SubEventItem, SubEventItemVariation + from .organizer import SalesChannel + + assert isinstance(channel, (str, SalesChannel)) qs = super().annotated(qs, channel, voucher=voucher) qs = qs.annotate( diff --git a/src/pretix/base/models/items.py b/src/pretix/base/models/items.py index 8c3fd3f408..04af185003 100644 --- a/src/pretix/base/models/items.py +++ b/src/pretix/base/models/items.py @@ -271,16 +271,24 @@ class SubEventItemVariation(models.Model): def filter_available(qs, channel='web', voucher=None, allow_addons=False): - assert isinstance(channel, str) + # Channel can currently be a SalesChannel or a str, since we need that compatibility, but a SalesChannel + # makes the query SIGNIFICANTLY faster + from .organizer import SalesChannel + + assert isinstance(channel, (SalesChannel, str)) q = ( # IMPORTANT: If this is updated, also update the ItemVariation query # in models/event.py: EventMixin.annotated() Q(active=True) & Q(Q(available_from__isnull=True) | Q(available_from__lte=time_machine_now()) | Q(available_from_mode='info')) & Q(Q(available_until__isnull=True) | Q(available_until__gte=time_machine_now()) | Q(available_until_mode='info')) - & Q(Q(all_sales_channels=True) | Q(limit_sales_channels__identifier=channel)) & Q(require_bundling=False) ) + if isinstance(channel, str): + q &= Q(Q(all_sales_channels=True) | Q(limit_sales_channels__identifier=channel)) + else: + q &= Q(Q(all_sales_channels=True) | Q(limit_sales_channels=channel)) + if not allow_addons: q &= Q(Q(category__isnull=True) | Q(category__is_addon=False)) diff --git a/src/pretix/presale/views/event.py b/src/pretix/presale/views/event.py index 1106dc16c4..4bc9d24c90 100644 --- a/src/pretix/presale/views/event.py +++ b/src/pretix/presale/views/event.py @@ -151,7 +151,7 @@ def get_grouped_items(event, *, channel: SalesChannel, subevent=None, voucher=No ), ).filter( variation_q, - Q(all_sales_channels=True) | Q(limit_sales_channels__identifier=channel.identifier), + Q(all_sales_channels=True) | Q(limit_sales_channels=channel), active=True, quotas__isnull=False, subevent_disabled=False @@ -685,7 +685,7 @@ class EventIndex(EventViewMixin, EventListMixin, CartMixin, TemplateView): add_subevents_for_days( filter_qs_by_attr( self.request.event.subevents_annotated( - self.request.sales_channel.identifier, + self.request.sales_channel, voucher, ).using(settings.DATABASE_REPLICA), self.request @@ -744,7 +744,7 @@ class EventIndex(EventViewMixin, EventListMixin, CartMixin, TemplateView): add_subevents_for_days( filter_qs_by_attr( self.request.event.subevents_annotated( - self.request.sales_channel.identifier, + self.request.sales_channel, voucher=voucher, ).using(settings.DATABASE_REPLICA), self.request @@ -793,7 +793,7 @@ class EventIndex(EventViewMixin, EventListMixin, CartMixin, TemplateView): context['subevent_list'] = self.request.event.subevents_sorted( filter_qs_by_attr( self.request.event.subevents_annotated( - self.request.sales_channel.identifier, + self.request.sales_channel, voucher=voucher, ).using(settings.DATABASE_REPLICA), self.request diff --git a/src/pretix/presale/views/organizer.py b/src/pretix/presale/views/organizer.py index 2b0ce53876..f518af43db 100644 --- a/src/pretix/presale/views/organizer.py +++ b/src/pretix/presale/views/organizer.py @@ -185,7 +185,7 @@ class EventListMixin: def _get_event_list_queryset(self): query = Q(is_public=True) & Q(live=True) qs = self.request.organizer.events.using(settings.DATABASE_REPLICA).filter(query) - qs = qs.filter(Q(all_sales_channels=True) | Q(limit_sales_channels__identifier=self.request.sales_channel.identifier)) + qs = qs.filter(Q(all_sales_channels=True) | Q(limit_sales_channels=self.request.sales_channel)) qs = qs.annotate( min_from=Min('subevents__date_from'), min_to=Min('subevents__date_to'), @@ -213,7 +213,7 @@ class EventListMixin: ).order_by('order_from') qs = Event.annotated(filter_qs_by_attr( qs, self.request, match_subevents_with_conditions=Q(active=True) & Q(is_public=True) & date_q - )) + ), self.request.sales_channel) return qs def _set_month_to_next_subevent(self): @@ -724,10 +724,10 @@ class CalendarView(OrganizerViewMixin, EventListMixin, TemplateView): ctx['has_before'], ctx['has_after'] = has_before_after( self.request.organizer.events.filter( - Q(all_sales_channels=True) | Q(limit_sales_channels__identifier=self.request.sales_channel.identifier), + Q(all_sales_channels=True) | Q(limit_sales_channels=self.request.sales_channel), ), SubEvent.objects.filter( - Q(event__all_sales_channels=True) | Q(event__limit_sales_channels__identifier=self.request.sales_channel.identifier), + Q(event__all_sales_channels=True) | Q(event__limit_sales_channels=self.request.sales_channel), event__organizer=self.request.organizer, event__is_public=True, event__live=True, @@ -746,14 +746,14 @@ class CalendarView(OrganizerViewMixin, EventListMixin, TemplateView): def _events_by_day(self, before, after): ebd = defaultdict(list) timezones = set() - add_events_for_days(self.request, Event.annotated(self.request.organizer.events, 'web').using( + add_events_for_days(self.request, Event.annotated(self.request.organizer.events, self.request.sales_channel).using( settings.DATABASE_REPLICA ).filter( - Q(all_sales_channels=True) | Q(limit_sales_channels__identifier=self.request.sales_channel.identifier), + Q(all_sales_channels=True) | Q(limit_sales_channels=self.request.sales_channel), ), before, after, ebd, timezones) add_subevents_for_days(filter_qs_by_attr(SubEvent.annotated(SubEvent.objects.filter( Q(event__all_sales_channels=True) | - Q(event__limit_sales_channels__identifier=self.request.sales_channel.identifier), + Q(event__limit_sales_channels=self.request.sales_channel), event__organizer=self.request.organizer, event__is_public=True, event__live=True, @@ -768,7 +768,7 @@ class CalendarView(OrganizerViewMixin, EventListMixin, TemplateView): ) ) ) - )), self.request).using(settings.DATABASE_REPLICA), before, after, ebd, timezones) + ), self.request.sales_channel), self.request).using(settings.DATABASE_REPLICA), before, after, ebd, timezones) self._multiple_timezones = len(timezones) > 1 return ebd @@ -807,11 +807,11 @@ class WeekCalendarView(OrganizerViewMixin, EventListMixin, TemplateView): ctx['has_before'], ctx['has_after'] = has_before_after( self.request.organizer.events.filter( - Q(all_sales_channels=True) | Q(limit_sales_channels__identifier=self.request.sales_channel.identifier), + Q(all_sales_channels=True) | Q(limit_sales_channels=self.request.sales_channel), ), SubEvent.objects.filter( Q(event__all_sales_channels=True) | - Q(event__limit_sales_channels__identifier=self.request.sales_channel.identifier), + Q(event__limit_sales_channels=self.request.sales_channel), event__organizer=self.request.organizer, event__is_public=True, event__live=True, @@ -842,14 +842,14 @@ class WeekCalendarView(OrganizerViewMixin, EventListMixin, TemplateView): def _events_by_day(self, before, after): ebd = defaultdict(list) timezones = set() - add_events_for_days(self.request, Event.annotated(self.request.organizer.events, 'web').using( + add_events_for_days(self.request, Event.annotated(self.request.organizer.events, self.request.sales_channel).using( settings.DATABASE_REPLICA ).filter( - Q(all_sales_channels=True) | Q(limit_sales_channels__identifier=self.request.sales_channel.identifier), + Q(all_sales_channels=True) | Q(limit_sales_channels=self.request.sales_channel), ), before, after, ebd, timezones) add_subevents_for_days(filter_qs_by_attr(SubEvent.annotated(SubEvent.objects.filter( Q(event__all_sales_channels=True) | - Q(event__limit_sales_channels__identifier=self.request.sales_channel.identifier), + Q(event__limit_sales_channels=self.request.sales_channel), event__organizer=self.request.organizer, event__is_public=True, event__live=True, @@ -864,7 +864,7 @@ class WeekCalendarView(OrganizerViewMixin, EventListMixin, TemplateView): ) ) ) - )), self.request).using(settings.DATABASE_REPLICA), before, after, ebd, timezones) + ), self.request.sales_channel), self.request).using(settings.DATABASE_REPLICA), before, after, ebd, timezones) self._multiple_timezones = len(timezones) > 1 return ebd @@ -946,11 +946,11 @@ class DayCalendarView(OrganizerViewMixin, EventListMixin, TemplateView): ctx['has_before'], ctx['has_after'] = has_before_after( self.request.organizer.events.filter( - Q(all_sales_channels=True) | Q(limit_sales_channels__identifier=self.request.sales_channel.identifier), + Q(all_sales_channels=True) | Q(limit_sales_channels=self.request.sales_channel), ), SubEvent.objects.filter( Q(event__all_sales_channels=True) | - Q(event__limit_sales_channels__identifier=self.request.sales_channel.identifier), + Q(event__limit_sales_channels=self.request.sales_channel), event__organizer=self.request.organizer, event__is_public=True, event__live=True, @@ -1194,14 +1194,14 @@ class DayCalendarView(OrganizerViewMixin, EventListMixin, TemplateView): def _events_by_day(self, before, after): ebd = defaultdict(list) timezones = set() - add_events_for_days(self.request, Event.annotated(self.request.organizer.events, 'web').using( + add_events_for_days(self.request, Event.annotated(self.request.organizer.events, self.request.sales_channel).using( settings.DATABASE_REPLICA ).filter( - Q(all_sales_channels=True) | Q(limit_sales_channels__identifier=self.request.sales_channel.identifier), + Q(all_sales_channels=True) | Q(limit_sales_channels=self.request.sales_channel), ), before, after, ebd, timezones) add_subevents_for_days(filter_qs_by_attr(SubEvent.annotated(SubEvent.objects.filter( Q(event__all_sales_channels=True) | - Q(event__limit_sales_channels__identifier=self.request.sales_channel.identifier), + Q(event__limit_sales_channels=self.request.sales_channel), event__organizer=self.request.organizer, event__is_public=True, event__live=True, @@ -1216,7 +1216,7 @@ class DayCalendarView(OrganizerViewMixin, EventListMixin, TemplateView): ) ) ) - )), self.request).using(settings.DATABASE_REPLICA), before, after, ebd, timezones) + ), self.request.sales_channel), self.request).using(settings.DATABASE_REPLICA), before, after, ebd, timezones) self._multiple_timezones = len(timezones) > 1 return ebd @@ -1229,7 +1229,7 @@ class OrganizerIcalDownload(OrganizerViewMixin, View): filter_qs_by_attr( self.request.organizer.events.filter( Q(date_from__gt=cutoff) | Q(date_to__gt=cutoff), - Q(all_sales_channels=True) | Q(limit_sales_channels__identifier=self.request.sales_channel.identifier), + Q(all_sales_channels=True) | Q(limit_sales_channels=self.request.sales_channel), is_public=True, live=True, has_subevents=False, @@ -1250,7 +1250,7 @@ class OrganizerIcalDownload(OrganizerViewMixin, View): SubEvent.objects.filter( Q(date_from__gt=cutoff) | Q(date_to__gt=cutoff), Q(event__all_sales_channels=True) | - Q(event__limit_sales_channels__identifier=self.request.sales_channel.identifier), + Q(event__limit_sales_channels=self.request.sales_channel), event__organizer=self.request.organizer, event__is_public=True, event__live=True, diff --git a/src/pretix/presale/views/widget.py b/src/pretix/presale/views/widget.py index 94af5e79e1..8e61c46d10 100644 --- a/src/pretix/presale/views/widget.py +++ b/src/pretix/presale/views/widget.py @@ -545,9 +545,9 @@ class WidgetAPIProductList(EventListMixin, View): if hasattr(self.request, 'event'): add_subevents_for_days( filter_qs_by_attr( - self.request.event.subevents_annotated('web').filter( + self.request.event.subevents_annotated(self.request.sales_channel).filter( Q(event__all_sales_channels=True) | - Q(event__limit_sales_channels__identifier=self.request.sales_channel.identifier), + Q(event__limit_sales_channels=self.request.sales_channel), ), self.request ), limit_before, after, ebd, set(), self.request.event, @@ -558,8 +558,8 @@ class WidgetAPIProductList(EventListMixin, View): add_events_for_days( self.request, filter_qs_by_attr( - Event.annotated(self.request.organizer.events, 'web').filter( - Q(all_sales_channels=True) | Q(limit_sales_channels__identifier=self.request.sales_channel.identifier), + Event.annotated(self.request.organizer.events, self.request.sales_channel).filter( + Q(all_sales_channels=True) | Q(limit_sales_channels=self.request.sales_channel), ), self.request ), limit_before, after, ebd, timezones @@ -572,7 +572,7 @@ class WidgetAPIProductList(EventListMixin, View): event__live=True, ).prefetch_related( 'event___settings_objects', 'event__organizer___settings_objects' - )), self.request), limit_before, after, ebd, timezones) + ), self.request.sales_channel), self.request), limit_before, after, ebd, timezones) data['weeks'] = weeks_for_template(ebd, self.year, self.month) for w in data['weeks']: @@ -605,7 +605,7 @@ class WidgetAPIProductList(EventListMixin, View): ebd = defaultdict(list) if hasattr(self.request, 'event'): add_subevents_for_days( - filter_qs_by_attr(self.request.event.subevents_annotated('web'), self.request), + filter_qs_by_attr(self.request.event.subevents_annotated(self.request.sales_channel), self.request), limit_before, after, ebd, set(), self.request.event, kwargs.get('cart_namespace') ) @@ -613,7 +613,7 @@ class WidgetAPIProductList(EventListMixin, View): timezones = set() add_events_for_days( self.request, - filter_qs_by_attr(Event.annotated(self.request.organizer.events, 'web'), self.request), + filter_qs_by_attr(Event.annotated(self.request.organizer.events, self.request.sales_channel), self.request), limit_before, after, ebd, timezones ) add_subevents_for_days(filter_qs_by_attr(SubEvent.annotated(SubEvent.objects.filter( @@ -622,7 +622,7 @@ class WidgetAPIProductList(EventListMixin, View): event__live=True, ).prefetch_related( 'event___settings_objects', 'event__organizer___settings_objects' - )), self.request), limit_before, after, ebd, timezones) + ), self.request.sales_channel), self.request), limit_before, after, ebd, timezones) data['days'] = days_for_template(ebd, week) for d in data['days']: @@ -632,7 +632,7 @@ class WidgetAPIProductList(EventListMixin, View): limit = 50 if hasattr(self.request, 'event'): evs = filter_qs_by_attr( - self.request.event.subevents_annotated(self.request.sales_channel.identifier), + self.request.event.subevents_annotated(self.request.sales_channel), self.request, match_subevents_with_conditions=( Q(Q(date_to__isnull=True) & Q(date_from__gte=now() - timedelta(hours=24))) diff --git a/src/tests/base/test_models.py b/src/tests/base/test_models.py index 5827e897dd..f61f13904d 100644 --- a/src/tests/base/test_models.py +++ b/src/tests/base/test_models.py @@ -2329,8 +2329,8 @@ class EventTest(TestCase): item2 = Item.objects.create(event=event, name='Early-bird ticket', default_price=0, active=False) q.items.add(item) q.items.add(item2) - assert Event.annotated(Event.objects).first().active_quotas == [q] - assert Event.annotated(Event.objects, 'foo').first().active_quotas == [] + assert Event.annotated(Event.objects, 'web').first().active_quotas == [q] + assert Event.annotated(Event.objects, self.organizer.sales_channels.get(identifier="bar")).first().active_quotas == [] @classscope(attr='organizer') def test_active_quotas_annotation_product_inactive(self): @@ -2341,7 +2341,7 @@ class EventTest(TestCase): q = Quota.objects.create(event=event, name='Quota', size=2) item = Item.objects.create(event=event, name='Early-bird ticket', default_price=0, active=False) q.items.add(item) - assert Event.annotated(Event.objects).first().active_quotas == [] + assert Event.annotated(Event.objects, 'web').first().active_quotas == [] @classscope(attr='organizer') def test_active_quotas_annotation_product_hidden_by_voucher(self): @@ -2354,16 +2354,16 @@ class EventTest(TestCase): q.items.add(item) voucher = Voucher.objects.create(event=event, code='a', item=item, show_hidden_items=True) - assert Event.annotated(Event.objects, voucher=voucher).first().active_quotas == [q] + assert Event.annotated(Event.objects, "web", voucher=voucher).first().active_quotas == [q] voucher = Voucher.objects.create(event=event, code='b', item=item, show_hidden_items=False) - assert Event.annotated(Event.objects, voucher=voucher).first().active_quotas == [] + assert Event.annotated(Event.objects, "web", voucher=voucher).first().active_quotas == [] voucher = Voucher.objects.create(event=event, code='c', show_hidden_items=True) - assert Event.annotated(Event.objects, voucher=voucher).first().active_quotas == [q] + assert Event.annotated(Event.objects, "web", voucher=voucher).first().active_quotas == [q] voucher = Voucher.objects.create(event=event, code='d', quota=q, show_hidden_items=True) - assert Event.annotated(Event.objects, voucher=voucher).first().active_quotas == [q] + assert Event.annotated(Event.objects, "web", voucher=voucher).first().active_quotas == [q] item2 = Item.objects.create(event=event, name='Early-bird ticket', default_price=0) var = item2.variations.create(item=item2, value='Test', hide_without_voucher=True) @@ -2373,13 +2373,13 @@ class EventTest(TestCase): q.variations.add(var) voucher = Voucher.objects.create(event=event, code='e', item=item2, variation=var, show_hidden_items=True) - assert Event.annotated(Event.objects, voucher=voucher).first().active_quotas == [q] + assert Event.annotated(Event.objects, "web", voucher=voucher).first().active_quotas == [q] voucher = Voucher.objects.create(event=event, code='f', item=item2, variation=var2, show_hidden_items=True) - assert Event.annotated(Event.objects, voucher=voucher).first().active_quotas == [] + assert Event.annotated(Event.objects, "web", voucher=voucher).first().active_quotas == [] voucher = Voucher.objects.create(event=event, code='g', quota=q, show_hidden_items=True) - assert Event.annotated(Event.objects, voucher=voucher).first().active_quotas == [q] + assert Event.annotated(Event.objects, "web", voucher=voucher).first().active_quotas == [q] @classscope(attr='organizer') def test_active_quotas_annotation_product_addon(self): @@ -2395,7 +2395,7 @@ class EventTest(TestCase): item.category = cat item.save() q.items.add(item) - assert Event.annotated(Event.objects).first().active_quotas == [] + assert Event.annotated(Event.objects, 'web').first().active_quotas == [] @classscope(attr='organizer') def test_active_quotas_annotation_product_unavailable(self): @@ -2407,7 +2407,7 @@ class EventTest(TestCase): item = Item.objects.create(event=event, name='Early-bird ticket', default_price=0, active=True, available_until=now() - timedelta(days=1)) q.items.add(item) - assert Event.annotated(Event.objects).first().active_quotas == [] + assert Event.annotated(Event.objects, 'web').first().active_quotas == [] @classscope(attr='organizer') def test_active_quotas_annotation_variation_not_in_quota(self): @@ -2419,7 +2419,7 @@ class EventTest(TestCase): item = Item.objects.create(event=event, name='Early-bird ticket', default_price=0, active=True) item.variations.create(value="foo") q.items.add(item) - assert Event.annotated(Event.objects).first().active_quotas == [] + assert Event.annotated(Event.objects, 'web').first().active_quotas == [] @classscope(attr='organizer') def test_active_quotas_annotation_variation(self): @@ -2434,29 +2434,29 @@ class EventTest(TestCase): v.limit_sales_channels.add(self.organizer.sales_channels.get(identifier="web")) q.items.add(item) q.variations.add(v) - assert Event.annotated(Event.objects).first().active_quotas == [q] + assert Event.annotated(Event.objects, 'web').first().active_quotas == [q] item.available_until = now() - timedelta(days=1) item.save() - assert Event.annotated(Event.objects).first().active_quotas == [] + assert Event.annotated(Event.objects, 'web').first().active_quotas == [] item.available_until = None item.available_from = now() + timedelta(days=1) item.save() - assert Event.annotated(Event.objects).first().active_quotas == [] + assert Event.annotated(Event.objects, 'web').first().active_quotas == [] item.available_until = None item.available_from = None item.active = False item.save() - assert Event.annotated(Event.objects).first().active_quotas == [] + assert Event.annotated(Event.objects, 'web').first().active_quotas == [] item.active = True item.save() - assert Event.annotated(Event.objects).first().active_quotas == [q] - assert Event.annotated(Event.objects, 'foo').first().active_quotas == [] + assert Event.annotated(Event.objects, 'web').first().active_quotas == [q] + assert Event.annotated(Event.objects, self.organizer.sales_channels.get(identifier="bar")).first().active_quotas == [] v.active = False v.save() - assert Event.annotated(Event.objects).first().active_quotas == [] + assert Event.annotated(Event.objects, 'web').first().active_quotas == [] item.hide_without_voucher = True item.save() - assert Event.annotated(Event.objects).first().active_quotas == [] + assert Event.annotated(Event.objects, 'web').first().active_quotas == [] class SubEventTest(TestCase): @@ -2502,8 +2502,10 @@ class SubEventTest(TestCase): all_sales_channels=False) item.limit_sales_channels.add(self.organizer.sales_channels.get(identifier="web")) q.items.add(item) - assert SubEvent.annotated(SubEvent.objects).first().active_quotas == [q] - assert SubEvent.annotated(SubEvent.objects, 'foo').first().active_quotas == [] + assert SubEvent.annotated(SubEvent.objects, 'web').first().active_quotas == [q] + assert SubEvent.annotated(SubEvent.objects, 'bar').first().active_quotas == [] + assert SubEvent.annotated(SubEvent.objects, self.organizer.sales_channels.get(identifier="web")).first().active_quotas == [q] + assert SubEvent.annotated(SubEvent.objects, self.organizer.sales_channels.get(identifier="bar")).first().active_quotas == [] @classscope(attr='organizer') def test_active_quotas_annotation_no_interference(self): @@ -2514,8 +2516,8 @@ class SubEventTest(TestCase): subevent=se2) item = Item.objects.create(event=self.event, name='Early-bird ticket', default_price=0, active=True) q.items.add(item) - assert SubEvent.annotated(SubEvent.objects).filter(pk=self.se.pk).first().active_quotas == [] - assert SubEvent.annotated(SubEvent.objects).filter(pk=se2.pk).first().active_quotas == [q] + assert SubEvent.annotated(SubEvent.objects, 'web').filter(pk=self.se.pk).first().active_quotas == [] + assert SubEvent.annotated(SubEvent.objects, 'web').filter(pk=se2.pk).first().active_quotas == [q] @classscope(attr='organizer') def test_best_availability(self): @@ -2540,7 +2542,7 @@ class SubEventTest(TestCase): q = Quota.objects.create(event=self.event, name='Quota', size=1, subevent=self.se) q.items.add(item) - obj = SubEvent.annotated(SubEvent.objects).first() + obj = SubEvent.annotated(SubEvent.objects, 'web').first() assert len(obj.active_quotas) == 1 assert obj.best_availability == (Quota.AVAILABILITY_GONE, 0, 1) @@ -2548,14 +2550,14 @@ class SubEventTest(TestCase): q2 = Quota.objects.create(event=self.event, name='Quota 2', size=2, subevent=self.se) q2.items.add(item) - obj = SubEvent.annotated(SubEvent.objects).first() + obj = SubEvent.annotated(SubEvent.objects, 'web').first() assert len(obj.active_quotas) == 2 assert obj.best_availability == (Quota.AVAILABILITY_GONE, 0, 1) # 2 quotas - 2 items. Higher quota wins since second item is only connected to second quota. item2 = Item.objects.create(event=self.event, name='Regular ticket', default_price=10, active=True) q2.items.add(item2) - obj = SubEvent.annotated(SubEvent.objects).first() + obj = SubEvent.annotated(SubEvent.objects, 'web').first() assert len(obj.active_quotas) == 2 assert obj.best_availability == (Quota.AVAILABILITY_OK, 1, 2) assert obj.best_availability_is_low @@ -2564,7 +2566,7 @@ class SubEventTest(TestCase): q.size = 10 q.save() q2.delete() - obj = SubEvent.annotated(SubEvent.objects).first() + obj = SubEvent.annotated(SubEvent.objects, 'web').first() assert len(obj.active_quotas) == 1 assert obj.best_availability == (Quota.AVAILABILITY_OK, 9, 10) assert not obj.best_availability_is_low @@ -2572,7 +2574,7 @@ class SubEventTest(TestCase): # Unlimited quota q.size = None q.save() - obj = SubEvent.annotated(SubEvent.objects).first() + obj = SubEvent.annotated(SubEvent.objects, 'web').first() assert obj.best_availability == (Quota.AVAILABILITY_OK, None, None) assert not obj.best_availability_is_low