Ensure total ordering of paginated lists (#3061)

This commit is contained in:
Raphael Michel
2023-02-24 10:51:51 +01:00
committed by GitHub
parent c2d720b3b9
commit 7d4b575150
26 changed files with 232 additions and 77 deletions

View File

@@ -24,10 +24,11 @@ from calendar import timegm
from django.db.models import Max
from django.http import HttpResponse
from django.utils.http import http_date, parse_http_date_safe
from rest_framework.filters import OrderingFilter
from pretix.api.pagination import TotalOrderingFilter
class RichOrderingFilter(OrderingFilter):
class RichOrderingFilter(TotalOrderingFilter):
def filter_queryset(self, request, queryset, view):
ordering = self.get_ordering(request, queryset, view)

View File

@@ -29,11 +29,11 @@ from django.utils.translation import gettext as _
from rest_framework import status, viewsets
from rest_framework.decorators import action
from rest_framework.exceptions import ValidationError
from rest_framework.filters import OrderingFilter
from rest_framework.mixins import CreateModelMixin, DestroyModelMixin
from rest_framework.response import Response
from rest_framework.serializers import as_serializer_error
from pretix.api.pagination import TotalOrderingFilter
from pretix.api.serializers.cart import (
CartPositionCreateSerializer, CartPositionSerializer,
)
@@ -47,7 +47,7 @@ from pretix.base.services.locking import NoLockManager
class CartPositionViewSet(CreateModelMixin, DestroyModelMixin, viewsets.ReadOnlyModelViewSet):
serializer_class = CartPositionSerializer
queryset = CartPosition.objects.none()
filter_backends = (OrderingFilter,)
filter_backends = (TotalOrderingFilter,)
ordering = ('datetime',)
ordering_fields = ('datetime', 'cart_id')
lookup_field = 'id'

View File

@@ -36,8 +36,8 @@ from django_filters.rest_framework import DjangoFilterBackend, FilterSet
from django_scopes import scopes_disabled
from rest_framework import viewsets
from rest_framework.exceptions import PermissionDenied
from rest_framework.filters import OrderingFilter
from pretix.api.pagination import TotalOrderingFilter
from pretix.api.serializers.discount import DiscountSerializer
from pretix.api.views import ConditionalListView
from pretix.base.models import CartPosition, Discount
@@ -52,7 +52,7 @@ with scopes_disabled():
class DiscountViewSet(ConditionalListView, viewsets.ModelViewSet):
serializer_class = DiscountSerializer
queryset = Discount.objects.none()
filter_backends = (DjangoFilterBackend, OrderingFilter)
filter_backends = (DjangoFilterBackend, TotalOrderingFilter)
filterset_class = DiscountFilter
ordering_fields = ('id', 'position')
ordering = ('position', 'id')

View File

@@ -39,11 +39,12 @@ from django.db.models import Prefetch, ProtectedError, Q
from django.utils.timezone import now
from django_filters.rest_framework import DjangoFilterBackend, FilterSet
from django_scopes import scopes_disabled
from rest_framework import filters, serializers, views, viewsets
from rest_framework import serializers, views, viewsets
from rest_framework.exceptions import PermissionDenied, ValidationError
from rest_framework.response import Response
from pretix.api.auth.permission import EventCRUDPermission
from pretix.api.pagination import TotalOrderingFilter
from pretix.api.serializers.event import (
CloneEventSerializer, DeviceEventSettingsSerializer, EventSerializer,
EventSettingsSerializer, SubEventSerializer, TaxRuleSerializer,
@@ -127,7 +128,7 @@ class EventViewSet(viewsets.ModelViewSet):
lookup_url_kwarg = 'event'
lookup_value_regex = '[^/]+'
permission_classes = (EventCRUDPermission,)
filter_backends = (DjangoFilterBackend, filters.OrderingFilter)
filter_backends = (DjangoFilterBackend, TotalOrderingFilter)
ordering = ('slug',)
ordering_fields = ('date_from', 'slug')
filterset_class = EventFilter
@@ -379,7 +380,7 @@ class SubEventViewSet(ConditionalListView, viewsets.ModelViewSet):
serializer_class = SubEventSerializer
queryset = SubEvent.objects.none()
write_permission = 'can_change_event_settings'
filter_backends = (DjangoFilterBackend, filters.OrderingFilter)
filter_backends = (DjangoFilterBackend, TotalOrderingFilter)
filterset_class = SubEventFilter
ordering = ('date_from',)
ordering_fields = ('id', 'date_from', 'last_modified')

View File

@@ -41,9 +41,9 @@ from django_scopes import scopes_disabled
from rest_framework import viewsets
from rest_framework.decorators import action
from rest_framework.exceptions import PermissionDenied
from rest_framework.filters import OrderingFilter
from rest_framework.response import Response
from pretix.api.pagination import TotalOrderingFilter
from pretix.api.serializers.item import (
ItemAddOnSerializer, ItemBundleSerializer, ItemCategorySerializer,
ItemSerializer, ItemVariationSerializer, QuestionOptionSerializer,
@@ -75,7 +75,7 @@ with scopes_disabled():
class ItemViewSet(ConditionalListView, viewsets.ModelViewSet):
serializer_class = ItemSerializer
queryset = Item.objects.none()
filter_backends = (DjangoFilterBackend, OrderingFilter)
filter_backends = (DjangoFilterBackend, TotalOrderingFilter)
ordering_fields = ('id', 'position')
ordering = ('position', 'id')
filterset_class = ItemFilter
@@ -138,7 +138,7 @@ class ItemViewSet(ConditionalListView, viewsets.ModelViewSet):
class ItemVariationViewSet(viewsets.ModelViewSet):
serializer_class = ItemVariationSerializer
queryset = ItemVariation.objects.none()
filter_backends = (DjangoFilterBackend, OrderingFilter,)
filter_backends = (DjangoFilterBackend, TotalOrderingFilter,)
ordering_fields = ('id', 'position')
ordering = ('id',)
permission = None
@@ -208,7 +208,7 @@ class ItemVariationViewSet(viewsets.ModelViewSet):
class ItemBundleViewSet(viewsets.ModelViewSet):
serializer_class = ItemBundleSerializer
queryset = ItemBundle.objects.none()
filter_backends = (DjangoFilterBackend, OrderingFilter,)
filter_backends = (DjangoFilterBackend, TotalOrderingFilter,)
ordering_fields = ('id',)
ordering = ('id',)
permission = None
@@ -260,7 +260,7 @@ class ItemBundleViewSet(viewsets.ModelViewSet):
class ItemAddOnViewSet(viewsets.ModelViewSet):
serializer_class = ItemAddOnSerializer
queryset = ItemAddOn.objects.none()
filter_backends = (DjangoFilterBackend, OrderingFilter,)
filter_backends = (DjangoFilterBackend, TotalOrderingFilter,)
ordering_fields = ('id', 'position')
ordering = ('id',)
permission = None
@@ -318,7 +318,7 @@ class ItemCategoryFilter(FilterSet):
class ItemCategoryViewSet(ConditionalListView, viewsets.ModelViewSet):
serializer_class = ItemCategorySerializer
queryset = ItemCategory.objects.none()
filter_backends = (DjangoFilterBackend, OrderingFilter)
filter_backends = (DjangoFilterBackend, TotalOrderingFilter)
filterset_class = ItemCategoryFilter
ordering_fields = ('id', 'position')
ordering = ('position', 'id')
@@ -373,7 +373,7 @@ with scopes_disabled():
class QuestionViewSet(ConditionalListView, viewsets.ModelViewSet):
serializer_class = QuestionSerializer
queryset = Question.objects.none()
filter_backends = (DjangoFilterBackend, OrderingFilter)
filter_backends = (DjangoFilterBackend, TotalOrderingFilter)
filterset_class = QuestionFilter
ordering_fields = ('id', 'position')
ordering = ('position', 'id')
@@ -418,7 +418,7 @@ class QuestionViewSet(ConditionalListView, viewsets.ModelViewSet):
class QuestionOptionViewSet(viewsets.ModelViewSet):
serializer_class = QuestionOptionSerializer
queryset = QuestionOption.objects.none()
filter_backends = (DjangoFilterBackend, OrderingFilter,)
filter_backends = (DjangoFilterBackend, TotalOrderingFilter,)
ordering_fields = ('id', 'position')
ordering = ('position',)
permission = None
@@ -475,7 +475,7 @@ with scopes_disabled():
class QuotaViewSet(ConditionalListView, viewsets.ModelViewSet):
serializer_class = QuotaSerializer
queryset = Quota.objects.none()
filter_backends = (DjangoFilterBackend, OrderingFilter,)
filter_backends = (DjangoFilterBackend, TotalOrderingFilter,)
filterset_class = QuotaFilter
ordering_fields = ('id', 'size')
ordering = ('id',)

View File

@@ -43,11 +43,11 @@ from rest_framework.decorators import action
from rest_framework.exceptions import (
APIException, NotFound, PermissionDenied, ValidationError,
)
from rest_framework.filters import OrderingFilter
from rest_framework.mixins import CreateModelMixin
from rest_framework.response import Response
from pretix.api.models import OAuthAccessToken
from pretix.api.pagination import TotalOrderingFilter
from pretix.api.serializers.order import (
BlockedTicketSecretSerializer, InvoiceSerializer, OrderCreateSerializer,
OrderPaymentCreateSerializer, OrderPaymentSerializer,
@@ -181,7 +181,7 @@ with scopes_disabled():
class OrderViewSet(viewsets.ModelViewSet):
serializer_class = OrderSerializer
queryset = Order.objects.none()
filter_backends = (DjangoFilterBackend, OrderingFilter)
filter_backends = (DjangoFilterBackend, TotalOrderingFilter)
ordering = ('datetime',)
ordering_fields = ('datetime', 'code', 'status', 'last_modified')
filterset_class = OrderFilter
@@ -1749,7 +1749,7 @@ class RetryException(APIException):
class InvoiceViewSet(viewsets.ReadOnlyModelViewSet):
serializer_class = InvoiceSerializer
queryset = Invoice.objects.none()
filter_backends = (DjangoFilterBackend, OrderingFilter)
filter_backends = (DjangoFilterBackend, TotalOrderingFilter)
ordering = ('nr',)
ordering_fields = ('nr', 'date')
filterset_class = InvoiceFilter
@@ -1842,7 +1842,7 @@ with scopes_disabled():
class RevokedSecretViewSet(viewsets.ReadOnlyModelViewSet):
serializer_class = RevokedTicketSecretSerializer
queryset = RevokedTicketSecret.objects.none()
filter_backends = (DjangoFilterBackend, OrderingFilter)
filter_backends = (DjangoFilterBackend, TotalOrderingFilter)
ordering = ('-created',)
ordering_fields = ('created', 'secret')
filterset_class = RevokedSecretFilter
@@ -1865,7 +1865,7 @@ with scopes_disabled():
class BlockedSecretViewSet(viewsets.ReadOnlyModelViewSet):
serializer_class = BlockedTicketSecretSerializer
queryset = BlockedTicketSecret.objects.none()
filter_backends = (DjangoFilterBackend, OrderingFilter)
filter_backends = (DjangoFilterBackend, TotalOrderingFilter)
ordering = ('-updated', '-pk')
filterset_class = BlockedSecretFilter
permission = 'can_view_orders'

View File

@@ -28,9 +28,7 @@ from django.shortcuts import get_object_or_404
from django.utils.functional import cached_property
from django_filters.rest_framework import DjangoFilterBackend, FilterSet
from django_scopes import scopes_disabled
from rest_framework import (
filters, mixins, serializers, status, views, viewsets,
)
from rest_framework import mixins, serializers, status, views, viewsets
from rest_framework.decorators import action
from rest_framework.exceptions import MethodNotAllowed, PermissionDenied
from rest_framework.mixins import CreateModelMixin, DestroyModelMixin
@@ -38,6 +36,7 @@ from rest_framework.response import Response
from rest_framework.viewsets import GenericViewSet
from pretix.api.models import OAuthAccessToken
from pretix.api.pagination import TotalOrderingFilter
from pretix.api.serializers.organizer import (
CustomerCreateSerializer, CustomerSerializer, DeviceSerializer,
GiftCardSerializer, GiftCardTransactionSerializer, MembershipSerializer,
@@ -62,7 +61,7 @@ class OrganizerViewSet(viewsets.ReadOnlyModelViewSet):
lookup_field = 'slug'
lookup_url_kwarg = 'organizer'
lookup_value_regex = '[^/]+'
filter_backends = (filters.OrderingFilter,)
filter_backends = (TotalOrderingFilter,)
ordering = ('slug',)
ordering_fields = ('name', 'slug')

View File

@@ -31,9 +31,9 @@ from django_scopes import scopes_disabled
from rest_framework import status, viewsets
from rest_framework.decorators import action
from rest_framework.exceptions import PermissionDenied
from rest_framework.filters import OrderingFilter
from rest_framework.response import Response
from pretix.api.pagination import TotalOrderingFilter
from pretix.api.serializers.voucher import VoucherSerializer
from pretix.base.models import Voucher
@@ -59,7 +59,7 @@ with scopes_disabled():
class VoucherViewSet(viewsets.ModelViewSet):
serializer_class = VoucherSerializer
queryset = Voucher.objects.none()
filter_backends = (DjangoFilterBackend, OrderingFilter)
filter_backends = (DjangoFilterBackend, TotalOrderingFilter)
ordering = ('id',)
ordering_fields = ('id', 'code', 'max_usages', 'valid_until', 'value')
filterset_class = VoucherFilter

View File

@@ -25,9 +25,9 @@ from django_scopes import scopes_disabled
from rest_framework import viewsets
from rest_framework.decorators import action
from rest_framework.exceptions import PermissionDenied, ValidationError
from rest_framework.filters import OrderingFilter
from rest_framework.response import Response
from pretix.api.pagination import TotalOrderingFilter
from pretix.api.serializers.waitinglist import WaitingListSerializer
from pretix.base.models import WaitingListEntry
from pretix.base.models.waitinglist import WaitingListException
@@ -47,8 +47,8 @@ with scopes_disabled():
class WaitingListViewSet(viewsets.ModelViewSet):
serializer_class = WaitingListSerializer
queryset = WaitingListEntry.objects.none()
filter_backends = (DjangoFilterBackend, OrderingFilter)
ordering = ('created',)
filter_backends = (DjangoFilterBackend, TotalOrderingFilter)
ordering = ('created', 'pk',)
ordering_fields = ('id', 'created', 'email', 'item')
filterset_class = WaitingListFilter
permission = 'can_view_orders'