diff --git a/doc/api/resources/seats.rst b/doc/api/resources/seats.rst new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/pretix/api/serializers/event.py b/src/pretix/api/serializers/event.py index 41199b6075..2d9e93e6e6 100644 --- a/src/pretix/api/serializers/event.py +++ b/src/pretix/api/serializers/event.py @@ -52,7 +52,7 @@ from pretix.api.serializers import ( from pretix.api.serializers.i18n import I18nAwareModelSerializer from pretix.api.serializers.settings import SettingsSerializer from pretix.base.models import ( - Device, Event, SalesChannel, TaxRule, TeamAPIToken, Seat, + Device, Event, SalesChannel, TaxRule, TeamAPIToken, Seat, CartPosition, Voucher, ) from pretix.base.models.event import SubEvent from pretix.base.models.items import ( @@ -971,20 +971,67 @@ class ItemMetaPropertiesSerializer(I18nAwareModelSerializer): fields = ('id', 'name', 'default', 'required', 'allowed_values') +def prefetch_by_id(items, manager, id_attr, id_filter, target_attr): + """ + Prefetches a related object on each item in the given list of items by searching by id or another + unique field. The id value is read from the attribute on item specified in `id_attr`, searched on manager by the + field specified in `id_filter`, and the resulting prefetched model object is stored into `target_attr` on the item. + """ + ids = [getattr(item, id_attr) for item in items if getattr(item, id_attr)] + if ids: + result = manager.order_by(id_filter).distinct(id_filter).in_bulk(id_list=ids, field_name=id_filter) + for item in items: + setattr(item, target_attr, result.get(getattr(item, id_attr))) + + class SeatSerializer(I18nAwareModelSerializer): - order = serializers.CharField() - cart = serializers.IntegerField() - voucher = serializers.IntegerField() + order = serializers.CharField(source='order_code') + cartposition = serializers.IntegerField(source='cartposition_id') + voucher = serializers.IntegerField(source='voucher_id') class Meta: model = Seat read_only_fields = ( 'id', 'subevent', 'zone_name', 'row_name', 'row_label', 'seat_number', 'seat_label', 'seat_guid', 'product', 'sorting_rank', 'x', 'y', - 'order', 'cart', 'voucher', + 'order', 'cartposition', 'voucher', ) fields = ( 'id', 'subevent', 'zone_name', 'row_name', 'row_label', 'seat_number', 'seat_label', 'seat_guid', 'product', 'blocked', 'sorting_rank', 'x', 'y', - 'order', 'cart', 'voucher', + 'order', 'cartposition', 'voucher', ) + + def prefetch_expanded_data(self, items, expand_fields, event): + if 'order' in expand_fields: + prefetch_by_id(items, event.organizer.orders.prefetch_related('positions'), 'order_code', 'code', 'order') + if 'cartposition' in expand_fields: + prefetch_by_id(items, CartPosition.objects, 'cartposition_id', 'id', 'cartposition') + if 'voucher' in expand_fields: + prefetch_by_id(items, Voucher.objects, 'voucher_id', 'id', 'voucher') + + def __init__(self, instance, *args, **kwargs): + if not kwargs.get('data'): + self.prefetch_expanded_data(instance if hasattr(instance, '__iter__') else [instance], + kwargs['context']['expand_fields'], + kwargs['context']['order_context']['event']) + + super().__init__(instance, *args, **kwargs) + + if 'order' in self.context['expand_fields']: + from pretix.api.serializers.order import OrderSerializer + self.fields['order'] = OrderSerializer(read_only=True, context=self.context['order_context']) + try: + del self.fields['order'].fields['positions'].child.fields['seat'] + except KeyError: + pass + + if 'cartposition' in self.context['expand_fields']: + from pretix.api.serializers.cart import CartPositionSerializer + self.fields['cartposition'] = CartPositionSerializer(read_only=True) + del self.fields['cartposition'].fields['seat'] + + if 'voucher' in self.context['expand_fields']: + from pretix.api.serializers.voucher import VoucherSerializer + self.fields['voucher'] = VoucherSerializer(read_only=True) + del self.fields['voucher'].fields['seat'] diff --git a/src/pretix/api/views/event.py b/src/pretix/api/views/event.py index 5034deefcd..ec22c6a12b 100644 --- a/src/pretix/api/views/event.py +++ b/src/pretix/api/views/event.py @@ -673,9 +673,8 @@ class SeatViewSet(ConditionalListView, viewsets.ModelViewSet): serializer_class = SeatSerializer queryset = Seat.objects.none() write_permission = 'can_change_event_settings' - filter_backends = [DjangoFilterBackend] - filterset_fields = ['zone_name', 'row_name', 'row_label', 'seat_number', 'seat_label', - 'seat_guid', 'blocked',] + filter_backends = (DjangoFilterBackend,) + filterset_fields = ('zone_name', 'row_name', 'row_label', 'seat_number', 'seat_label', 'seat_guid', 'blocked',) def get_queryset(self): if self.request.event.has_subevents and 'subevent' in self.request.resolver_match.kwargs: @@ -683,11 +682,25 @@ class SeatViewSet(ConditionalListView, viewsets.ModelViewSet): subevent = self.request.event.subevents.get(pk=self.request.resolver_match.kwargs['subevent']) except SubEvent.DoesNotExist: raise NotFound('Subevent not found') - return Seat.annotated(event_id=self.request.event.id, subevent=subevent, qs=subevent.seats.all(), annotate_ids=True) + qs = Seat.annotated(event_id=self.request.event.id, subevent=subevent, qs=subevent.seats.all(), annotate_ids=True) elif not self.request.event.has_subevents and 'subevent' not in self.request.resolver_match.kwargs: - return Seat.annotated(event_id=self.request.event.id, subevent=None, qs=self.request.event.seats.all(), annotate_ids=True) + qs = Seat.annotated(event_id=self.request.event.id, subevent=None, qs=self.request.event.seats.all(), annotate_ids=True) else: - raise NotFound + raise NotFound('Please use the subevent-specific endpoint' if self.request.event.has_subevents + else 'This event has no subevents') + + return qs + + def get_serializer_context(self): + ctx = super().get_serializer_context() + ctx['expand_fields'] = self.request.query_params.getlist('expand') + ctx['order_context'] = { + 'event': self.request.event, + 'pdf_data': None, + 'include': self.request.query_params.getlist('order_include'), + 'exclude': self.request.query_params.getlist('order_exclude'), + } + return ctx def perform_update(self, serializer): super().perform_update(serializer) diff --git a/src/pretix/base/models/seating.py b/src/pretix/base/models/seating.py index ee5970ea2c..4e95078bf4 100644 --- a/src/pretix/base/models/seating.py +++ b/src/pretix/base/models/seating.py @@ -216,9 +216,9 @@ class Seat(models.Model): cqs = cqs.exclude(cart_id=ignore_cart_id) if annotate_ids: qs_annotated = qs.annotate( - order=Subquery(opqs.values('order__code')), - cart=Subquery(cqs.values('id')), - voucher=Subquery(vqs.values('id')), + order_code=Subquery(opqs.values('order__code')), + cartposition_id=Subquery(cqs.values('id')), + voucher_id=Subquery(vqs.values('id')), ) else: qs_annotated = qs.annotate(