diff --git a/src/pretix/api/middleware.py b/src/pretix/api/middleware.py index 2e9961138..c75623ae0 100644 --- a/src/pretix/api/middleware.py +++ b/src/pretix/api/middleware.py @@ -21,15 +21,15 @@ class IdempotencyMiddleware: if not request.path.startswith('/api/'): return self.get_response(request) - if not request.META.get('HTTP_X_IDEMPOTENCY_KEY'): + if not request.headers.get('X-Idempotency-Key'): return self.get_response(request) auth_hash_parts = '{}:{}'.format( - request.META.get('HTTP_AUTHORIZATION', ''), + request.headers.get('Authorization', ''), request.COOKIES.get(settings.SESSION_COOKIE_NAME, '') ) auth_hash = sha1(auth_hash_parts.encode()).hexdigest() - idempotency_key = request.META.get('HTTP_X_IDEMPOTENCY_KEY', '') + idempotency_key = request.headers.get('X-Idempotency-Key', '') with transaction.atomic(): call, created = ApiCall.objects.select_for_update().get_or_create( diff --git a/src/pretix/api/models.py b/src/pretix/api/models.py index 7f63b9e5e..87e23febb 100644 --- a/src/pretix/api/models.py +++ b/src/pretix/api/models.py @@ -77,6 +77,9 @@ class WebHook(models.Model): all_events = models.BooleanField(default=True, verbose_name=_("All events (including newly created ones)")) limit_events = models.ManyToManyField('pretixbase.Event', verbose_name=_("Limit to events"), blank=True) + class Meta: + ordering = ('id',) + @property def action_types(self): return [ diff --git a/src/pretix/api/views/__init__.py b/src/pretix/api/views/__init__.py index c26f6ef1e..45f5f7e44 100644 --- a/src/pretix/api/views/__init__.py +++ b/src/pretix/api/views/__init__.py @@ -31,10 +31,10 @@ class RichOrderingFilter(OrderingFilter): class ConditionalListView: def list(self, request, **kwargs): - if_modified_since = request.META.get('HTTP_IF_MODIFIED_SINCE') + if_modified_since = request.headers.get('If-Modified-Since') if if_modified_since: if_modified_since = parse_http_date_safe(if_modified_since) - if_unmodified_since = request.META.get('HTTP_IF_UNMODIFIED_SINCE') + if_unmodified_since = request.headers.get('If-Unmodified-Since') if if_unmodified_since: if_unmodified_since = parse_http_date_safe(if_unmodified_since) if not hasattr(request, 'event'): diff --git a/src/pretix/api/views/checkin.py b/src/pretix/api/views/checkin.py index e1a2dbcd6..5c78fac20 100644 --- a/src/pretix/api/views/checkin.py +++ b/src/pretix/api/views/checkin.py @@ -7,7 +7,7 @@ from django.utils.functional import cached_property from django.utils.timezone import now from django_filters.rest_framework import DjangoFilterBackend, FilterSet from rest_framework import viewsets -from rest_framework.decorators import detail_route +from rest_framework.decorators import action from rest_framework.fields import DateTimeField from rest_framework.response import Response @@ -77,7 +77,7 @@ class CheckinListViewSet(viewsets.ModelViewSet): ) super().perform_destroy(instance) - @detail_route(methods=['GET']) + @action(detail=True, methods=['GET']) def status(self, *args, **kwargs): clist = self.get_object() cqs = Checkin.objects.filter( @@ -242,7 +242,7 @@ class CheckinListPositionViewSet(viewsets.ReadOnlyModelViewSet): return qs - @detail_route(methods=['POST']) + @action(detail=True, methods=['POST']) def redeem(self, *args, **kwargs): force = bool(self.request.data.get('force', False)) ignore_unpaid = bool(self.request.data.get('ignore_unpaid', False)) diff --git a/src/pretix/api/views/item.py b/src/pretix/api/views/item.py index e271d3361..ae3fb3f64 100644 --- a/src/pretix/api/views/item.py +++ b/src/pretix/api/views/item.py @@ -4,7 +4,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 rest_framework import viewsets -from rest_framework.decorators import detail_route +from rest_framework.decorators import action from rest_framework.exceptions import PermissionDenied from rest_framework.filters import OrderingFilter from rest_framework.response import Response @@ -499,7 +499,7 @@ class QuotaViewSet(ConditionalListView, viewsets.ModelViewSet): ) super().perform_destroy(instance) - @detail_route(methods=['get']) + @action(detail=True, methods=['get']) def availability(self, request, *args, **kwargs): quota = self.get_object() diff --git a/src/pretix/api/views/order.py b/src/pretix/api/views/order.py index 5182b6c29..75d664c48 100644 --- a/src/pretix/api/views/order.py +++ b/src/pretix/api/views/order.py @@ -12,7 +12,7 @@ from django.utils.timezone import make_aware, now from django.utils.translation import ugettext as _ from django_filters.rest_framework import DjangoFilterBackend, FilterSet from rest_framework import mixins, serializers, status, viewsets -from rest_framework.decorators import detail_route +from rest_framework.decorators import action from rest_framework.exceptions import ( APIException, NotFound, PermissionDenied, ValidationError, ) @@ -127,7 +127,7 @@ class OrderViewSet(viewsets.ModelViewSet): serializer = self.get_serializer(queryset, many=True) return Response(serializer.data, headers={'X-Page-Generated': date}) - @detail_route(url_name='download', url_path='download/(?P[^/]+)') + @action(detail=True, url_name='download', url_path='download/(?P[^/]+)') def download(self, request, output, **kwargs): provider = self._get_output_provider(output) order = self.get_object() @@ -149,7 +149,7 @@ class OrderViewSet(viewsets.ModelViewSet): ) return resp - @detail_route(methods=['POST']) + @action(detail=True, methods=['POST']) def mark_paid(self, request, **kwargs): order = self.get_object() @@ -190,7 +190,7 @@ class OrderViewSet(viewsets.ModelViewSet): status=status.HTTP_400_BAD_REQUEST ) - @detail_route(methods=['POST']) + @action(detail=True, methods=['POST']) def mark_canceled(self, request, **kwargs): send_mail = request.data.get('send_email', True) cancellation_fee = request.data.get('cancellation_fee', None) @@ -224,7 +224,7 @@ class OrderViewSet(viewsets.ModelViewSet): ) return self.retrieve(request, [], **kwargs) - @detail_route(methods=['POST']) + @action(detail=True, methods=['POST']) def approve(self, request, **kwargs): send_mail = request.data.get('send_email', True) @@ -242,7 +242,7 @@ class OrderViewSet(viewsets.ModelViewSet): return Response({'detail': str(e)}, status=status.HTTP_400_BAD_REQUEST) return self.retrieve(request, [], **kwargs) - @detail_route(methods=['POST']) + @action(detail=True, methods=['POST']) def deny(self, request, **kwargs): send_mail = request.data.get('send_email', True) comment = request.data.get('comment', '') @@ -260,7 +260,7 @@ class OrderViewSet(viewsets.ModelViewSet): return Response({'detail': str(e)}, status=status.HTTP_400_BAD_REQUEST) return self.retrieve(request, [], **kwargs) - @detail_route(methods=['POST']) + @action(detail=True, methods=['POST']) def mark_pending(self, request, **kwargs): order = self.get_object() @@ -279,7 +279,7 @@ class OrderViewSet(viewsets.ModelViewSet): ) return self.retrieve(request, [], **kwargs) - @detail_route(methods=['POST']) + @action(detail=True, methods=['POST']) def mark_expired(self, request, **kwargs): order = self.get_object() @@ -296,7 +296,7 @@ class OrderViewSet(viewsets.ModelViewSet): ) return self.retrieve(request, [], **kwargs) - @detail_route(methods=['POST']) + @action(detail=True, methods=['POST']) def mark_refunded(self, request, **kwargs): order = self.get_object() @@ -313,7 +313,7 @@ class OrderViewSet(viewsets.ModelViewSet): ) return self.retrieve(request, [], **kwargs) - @detail_route(methods=['POST']) + @action(detail=True, methods=['POST']) def create_invoice(self, request, **kwargs): order = self.get_object() has_inv = order.invoices.exists() and not ( @@ -345,7 +345,7 @@ class OrderViewSet(viewsets.ModelViewSet): status=status.HTTP_201_CREATED ) - @detail_route(methods=['POST']) + @action(detail=True, methods=['POST']) def resend_link(self, request, **kwargs): order = self.get_object() if not order.email: @@ -359,7 +359,7 @@ class OrderViewSet(viewsets.ModelViewSet): status=status.HTTP_204_NO_CONTENT ) - @detail_route(methods=['POST']) + @action(detail=True, methods=['POST']) @transaction.atomic def regenerate_secrets(self, request, **kwargs): order = self.get_object() @@ -377,7 +377,7 @@ class OrderViewSet(viewsets.ModelViewSet): ) return self.retrieve(request, [], **kwargs) - @detail_route(methods=['POST']) + @action(detail=True, methods=['POST']) def extend(self, request, **kwargs): new_date = request.data.get('expires', None) force = request.data.get('force', False) @@ -619,7 +619,7 @@ class OrderPositionViewSet(mixins.DestroyModelMixin, viewsets.ReadOnlyModelViewS return prov raise NotFound('Unknown output provider.') - @detail_route(url_name='download', url_path='download/(?P[^/]+)') + @action(detail=True, url_name='download', url_path='download/(?P[^/]+)') def download(self, request, output, **kwargs): provider = self._get_output_provider(output) pos = self.get_object() @@ -670,7 +670,7 @@ class PaymentViewSet(viewsets.ReadOnlyModelViewSet): order = get_object_or_404(Order, code=self.kwargs['order'], event=self.request.event) return order.payments.all() - @detail_route(methods=['POST']) + @action(detail=True, methods=['POST']) def confirm(self, request, **kwargs): payment = self.get_object() force = request.data.get('force', False) @@ -691,7 +691,7 @@ class PaymentViewSet(viewsets.ReadOnlyModelViewSet): pass return self.retrieve(request, [], **kwargs) - @detail_route(methods=['POST']) + @action(detail=True, methods=['POST']) def refund(self, request, **kwargs): payment = self.get_object() amount = serializers.DecimalField(max_digits=10, decimal_places=2).to_internal_value( @@ -756,7 +756,7 @@ class PaymentViewSet(viewsets.ReadOnlyModelViewSet): payment.order.save(update_fields=['status', 'expires']) return Response(OrderRefundSerializer(r).data, status=status.HTTP_200_OK) - @detail_route(methods=['POST']) + @action(detail=True, methods=['POST']) def cancel(self, request, **kwargs): payment = self.get_object() @@ -784,7 +784,7 @@ class RefundViewSet(CreateModelMixin, viewsets.ReadOnlyModelViewSet): order = get_object_or_404(Order, code=self.kwargs['order'], event=self.request.event) return order.refunds.all() - @detail_route(methods=['POST']) + @action(detail=True, methods=['POST']) def cancel(self, request, **kwargs): refund = self.get_object() @@ -801,7 +801,7 @@ class RefundViewSet(CreateModelMixin, viewsets.ReadOnlyModelViewSet): }, user=self.request.user if self.request.user.is_authenticated else None, auth=self.request.auth) return self.retrieve(request, [], **kwargs) - @detail_route(methods=['POST']) + @action(detail=True, methods=['POST']) def process(self, request, **kwargs): refund = self.get_object() @@ -826,7 +826,7 @@ class RefundViewSet(CreateModelMixin, viewsets.ReadOnlyModelViewSet): refund.order.save(update_fields=['status', 'expires']) return self.retrieve(request, [], **kwargs) - @detail_route(methods=['POST']) + @action(detail=True, methods=['POST']) def done(self, request, **kwargs): refund = self.get_object() @@ -916,7 +916,7 @@ class InvoiceViewSet(viewsets.ReadOnlyModelViewSet): nr=Concat('prefix', 'invoice_no') ) - @detail_route() + @action(detail=True, ) def download(self, request, **kwargs): invoice = self.get_object() @@ -934,7 +934,7 @@ class InvoiceViewSet(viewsets.ReadOnlyModelViewSet): resp['Content-Disposition'] = 'attachment; filename="{}.pdf"'.format(invoice.number) return resp - @detail_route(methods=['POST']) + @action(detail=True, methods=['POST']) def regenerate(self, request, **kwarts): inv = self.get_object() if inv.canceled: @@ -953,7 +953,7 @@ class InvoiceViewSet(viewsets.ReadOnlyModelViewSet): ) return Response(status=204) - @detail_route(methods=['POST']) + @action(detail=True, methods=['POST']) def reissue(self, request, **kwarts): inv = self.get_object() if inv.canceled: diff --git a/src/pretix/api/views/voucher.py b/src/pretix/api/views/voucher.py index 4f2bd5972..187a04837 100644 --- a/src/pretix/api/views/voucher.py +++ b/src/pretix/api/views/voucher.py @@ -7,7 +7,7 @@ from django_filters.rest_framework import ( BooleanFilter, DjangoFilterBackend, FilterSet, ) from rest_framework import status, viewsets -from rest_framework.decorators import list_route +from rest_framework.decorators import action from rest_framework.exceptions import PermissionDenied from rest_framework.filters import OrderingFilter from rest_framework.response import Response @@ -116,7 +116,7 @@ class VoucherViewSet(viewsets.ModelViewSet): instance.cartposition_set.all().delete() super().perform_destroy(instance) - @list_route(methods=['POST']) + @action(detail=False, methods=['POST']) def batch_create(self, request, *args, **kwargs): if any(self._predict_quota_check(d, None) for d in request.data): lockfn = request.event.lock diff --git a/src/pretix/api/views/waitinglist.py b/src/pretix/api/views/waitinglist.py index cf649dc43..d8bb51629 100644 --- a/src/pretix/api/views/waitinglist.py +++ b/src/pretix/api/views/waitinglist.py @@ -1,7 +1,7 @@ import django_filters from django_filters.rest_framework import DjangoFilterBackend, FilterSet from rest_framework import viewsets -from rest_framework.decorators import detail_route +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 @@ -69,7 +69,7 @@ class WaitingListViewSet(viewsets.ModelViewSet): ) super().perform_destroy(instance) - @detail_route(methods=['POST']) + @action(detail=True, methods=['POST']) def send_voucher(self, *args, **kwargs): try: self.get_object().send_voucher( diff --git a/src/pretix/base/middleware.py b/src/pretix/base/middleware.py index 291944879..96225d2bb 100644 --- a/src/pretix/base/middleware.py +++ b/src/pretix/base/middleware.py @@ -97,7 +97,7 @@ def get_language_from_event(request: HttpRequest) -> str: def get_language_from_browser(request: HttpRequest) -> str: - accept = request.META.get('HTTP_ACCEPT_LANGUAGE', '') + accept = request.headers.get('Accept-Language', '') for accept_lang, unused in parse_accept_lang_header(accept): if accept_lang == '*': break diff --git a/src/pretix/base/models/auth.py b/src/pretix/base/models/auth.py index d2263da56..414f895d6 100644 --- a/src/pretix/base/models/auth.py +++ b/src/pretix/base/models/auth.py @@ -111,6 +111,7 @@ class User(AbstractBaseUser, PermissionsMixin, LoggingMixin): class Meta: verbose_name = _("User") verbose_name_plural = _("Users") + ordering = ('email',) def save(self, *args, **kwargs): self.email = self.email.lower() diff --git a/src/pretix/base/models/fields.py b/src/pretix/base/models/fields.py index 67c782fc9..76852b6b4 100644 --- a/src/pretix/base/models/fields.py +++ b/src/pretix/base/models/fields.py @@ -37,7 +37,7 @@ class MultiStringField(TextField): def get_prep_lookup(self, lookup_type, value): # NOQA raise TypeError('Lookups on multi strings are currently not supported.') - def from_db_value(self, value, expression, connection, context): + def from_db_value(self, value, expression, connection): if value: return [v for v in value.split(DELIMITER) if v] else: diff --git a/src/pretix/base/models/tax.py b/src/pretix/base/models/tax.py index 76f4a9db5..303873dab 100644 --- a/src/pretix/base/models/tax.py +++ b/src/pretix/base/models/tax.py @@ -118,6 +118,9 @@ class TaxRule(LoggedModel): ) custom_rules = models.TextField(blank=True, null=True) + class Meta: + ordering = ('event', 'rate', 'id') + def allow_delete(self): from pretix.base.models.orders import OrderFee, OrderPosition diff --git a/src/pretix/base/services/invoices.py b/src/pretix/base/services/invoices.py index f765a20c7..a9f1b9464 100644 --- a/src/pretix/base/services/invoices.py +++ b/src/pretix/base/services/invoices.py @@ -120,7 +120,7 @@ def build_invoice(invoice: Invoice) -> Invoice: positions = list( invoice.order.positions.select_related('addon_to', 'item', 'tax_rule', 'subevent', 'variation').annotate( addon_c=Count('addons') - ) + ).order_by('positionid', 'id') ) reverse_charge = False diff --git a/src/pretix/base/templates/400.html b/src/pretix/base/templates/400.html index a86a2d3f1..6eadd1a70 100644 --- a/src/pretix/base/templates/400.html +++ b/src/pretix/base/templates/400.html @@ -1,6 +1,6 @@ {% extends "error.html" %} {% load i18n %} -{% load staticfiles %} +{% load static %} {% block title %}{% trans "Bad Request" %}{% endblock %} {% block content %} diff --git a/src/pretix/base/templates/403.html b/src/pretix/base/templates/403.html index 28df94c6b..edadd07a9 100644 --- a/src/pretix/base/templates/403.html +++ b/src/pretix/base/templates/403.html @@ -1,6 +1,6 @@ {% extends "error.html" %} {% load i18n %} -{% load staticfiles %} +{% load static %} {% block title %}{% trans "Permission denied" %}{% endblock %} {% block content %} diff --git a/src/pretix/base/templates/404.html b/src/pretix/base/templates/404.html index b157a5872..762b9b447 100644 --- a/src/pretix/base/templates/404.html +++ b/src/pretix/base/templates/404.html @@ -1,6 +1,6 @@ {% extends "error.html" %} {% load i18n %} -{% load staticfiles %} +{% load static %} {% block title %}{% trans "Not found" %}{% endblock %} {% block content %} diff --git a/src/pretix/base/templates/500.html b/src/pretix/base/templates/500.html index c99d6ce45..f8b01ed13 100644 --- a/src/pretix/base/templates/500.html +++ b/src/pretix/base/templates/500.html @@ -1,6 +1,6 @@ {% extends "error.html" %} {% load i18n %} -{% load staticfiles %} +{% load static %} {% block title %}{% trans "Internal Server Error" %}{% endblock %} {% block content %} diff --git a/src/pretix/base/templates/csrffail.html b/src/pretix/base/templates/csrffail.html index bdd8642a6..7710effc1 100644 --- a/src/pretix/base/templates/csrffail.html +++ b/src/pretix/base/templates/csrffail.html @@ -1,6 +1,6 @@ {% extends "error.html" %} {% load i18n %} -{% load staticfiles %} +{% load static %} {% block title %}{% trans "Verification failed" %}{% endblock %} {% block content %} diff --git a/src/pretix/base/templates/error.html b/src/pretix/base/templates/error.html index fcb1f223c..ad1e120a7 100644 --- a/src/pretix/base/templates/error.html +++ b/src/pretix/base/templates/error.html @@ -1,6 +1,6 @@ {% load compress %} {% load i18n %} -{% load staticfiles %} +{% load static %} diff --git a/src/pretix/base/views/js_catalog.py b/src/pretix/base/views/js_catalog.py index ea315aaec..7eedfe4dd 100644 --- a/src/pretix/base/views/js_catalog.py +++ b/src/pretix/base/views/js_catalog.py @@ -2,7 +2,7 @@ from django.utils import timezone from django.utils.translation.trans_real import DjangoTranslation from django.views.decorators.cache import cache_page from django.views.decorators.http import etag -from django.views.i18n import JavaScriptCatalog, render_javascript_catalog +from django.views.i18n import JavaScriptCatalog # Yes, we want to regenerate this every time the module has been imported to # refresh the cache at least at every code deployment @@ -21,4 +21,5 @@ js_info_dict = { def js_catalog(request, lang): c = JavaScriptCatalog() c.translation = DjangoTranslation(lang, domain='djangojs') - return render_javascript_catalog(c.get_catalog(), c.get_plural()) + context = c.get_context_data() + return c.render_to_response(context) diff --git a/src/pretix/base/views/metrics.py b/src/pretix/base/views/metrics.py index bf5a796c1..f94479b46 100644 --- a/src/pretix/base/views/metrics.py +++ b/src/pretix/base/views/metrics.py @@ -20,10 +20,10 @@ def serve_metrics(request): return unauthed_response() # check if the user is properly authorized: - if "HTTP_AUTHORIZATION" not in request.META: + if "Authorization" not in request.headers: return unauthed_response() - method, credentials = request.META["HTTP_AUTHORIZATION"].split(" ", 1) + method, credentials = request.headers["Authorization"].split(" ", 1) if method.lower() != "basic": return unauthed_response() diff --git a/src/pretix/control/templates/pretixcontrol/auth/base.html b/src/pretix/control/templates/pretixcontrol/auth/base.html index 4a55d8a84..6f27cf29e 100644 --- a/src/pretix/control/templates/pretixcontrol/auth/base.html +++ b/src/pretix/control/templates/pretixcontrol/auth/base.html @@ -1,6 +1,6 @@ {% load compress %} {% load i18n %} -{% load staticfiles %} +{% load static %} diff --git a/src/pretix/control/templates/pretixcontrol/organizers/device_connect.html b/src/pretix/control/templates/pretixcontrol/organizers/device_connect.html index c7ce67ff2..ee91f73d1 100644 --- a/src/pretix/control/templates/pretixcontrol/organizers/device_connect.html +++ b/src/pretix/control/templates/pretixcontrol/organizers/device_connect.html @@ -1,6 +1,6 @@ {% extends "pretixcontrol/organizers/base.html" %} {% load i18n %} -{% load staticfiles %} +{% load static %} {% load bootstrap3 %} {% block inner %}

{% trans "Connect to device:" %} {{ device.name }}

diff --git a/src/pretix/control/views/item.py b/src/pretix/control/views/item.py index 851848c3a..baddaa4e6 100644 --- a/src/pretix/control/views/item.py +++ b/src/pretix/control/views/item.py @@ -49,7 +49,9 @@ class ItemList(ListView): event=self.request.event ).annotate( var_count=Count('variations') - ).prefetch_related("category") + ).prefetch_related("category").order_by( + 'category__position', 'category', 'position' + ) def item_move(request, item, up=True): diff --git a/src/pretix/helpers/security.py b/src/pretix/helpers/security.py index 6e319804e..798202d96 100644 --- a/src/pretix/helpers/security.py +++ b/src/pretix/helpers/security.py @@ -13,7 +13,7 @@ class SessionReauthRequired(Exception): def get_user_agent_hash(request): - return hashlib.sha256(request.META['HTTP_USER_AGENT'].encode()).hexdigest() + return hashlib.sha256(request.headers['User-Agent'].encode()).hexdigest() def assert_session_valid(request): @@ -26,7 +26,7 @@ def assert_session_valid(request): if time.time() - last_used > settings.PRETIX_SESSION_TIMEOUT_RELATIVE: raise SessionReauthRequired() - if 'HTTP_USER_AGENT' in request.META: + if 'User-Agent' in request.headers: if 'pinned_user_agent' in request.session: if request.session.get('pinned_user_agent') != get_user_agent_hash(request): raise SessionInvalid() diff --git a/src/pretix/multidomain/middlewares.py b/src/pretix/multidomain/middlewares.py index cc5570ed7..fa366e5c4 100644 --- a/src/pretix/multidomain/middlewares.py +++ b/src/pretix/multidomain/middlewares.py @@ -23,10 +23,10 @@ LOCAL_HOST_NAMES = ('testserver', 'localhost') class MultiDomainMiddleware(MiddlewareMixin): def process_request(self, request): # We try three options, in order of decreasing preference. - if settings.USE_X_FORWARDED_HOST and ('HTTP_X_FORWARDED_HOST' in request.META): - host = request.META['HTTP_X_FORWARDED_HOST'] - elif 'HTTP_HOST' in request.META: - host = request.META['HTTP_HOST'] + if settings.USE_X_FORWARDED_HOST and ('X-Forwarded-Host' in request.headers): + host = request.headers['X-Forwarded-Host'] + elif 'Host' in request.headers: + host = request.headers['Host'] else: # Reconstruct the host using the algorithm from PEP 333. host = request.META['SERVER_NAME'] diff --git a/src/pretix/plugins/badges/models.py b/src/pretix/plugins/badges/models.py index dadce25eb..57f32daee 100644 --- a/src/pretix/plugins/badges/models.py +++ b/src/pretix/plugins/badges/models.py @@ -52,3 +52,6 @@ class BadgeItem(models.Model): on_delete=models.CASCADE) layout = models.ForeignKey('BadgeLayout', on_delete=models.CASCADE, related_name='item_assignments', null=True, blank=True) + + class Meta: + ordering = ('id',) diff --git a/src/pretix/plugins/banktransfer/models.py b/src/pretix/plugins/banktransfer/models.py index ed1b239ae..23fde5245 100644 --- a/src/pretix/plugins/banktransfer/models.py +++ b/src/pretix/plugins/banktransfer/models.py @@ -21,6 +21,9 @@ class BankImportJob(models.Model): created = models.DateTimeField(auto_now_add=True) state = models.CharField(max_length=32, choices=STATES, default=STATE_PENDING) + class Meta: + ordering = ('id',) + @property def owner_kwargs(self): if self.event: @@ -76,3 +79,4 @@ class BankTransaction(models.Model): class Meta: unique_together = ('event', 'organizer', 'checksum') + ordering = ('date', 'id') diff --git a/src/pretix/plugins/ticketoutputpdf/models.py b/src/pretix/plugins/ticketoutputpdf/models.py index c8a9ecc57..4ed9a7172 100644 --- a/src/pretix/plugins/ticketoutputpdf/models.py +++ b/src/pretix/plugins/ticketoutputpdf/models.py @@ -73,3 +73,4 @@ class TicketLayoutItem(models.Model): class Meta: unique_together = (('item', 'layout', 'sales_channel'),) + ordering = ("id",) diff --git a/src/pretix/presale/views/locale.py b/src/pretix/presale/views/locale.py index 3160c8940..7b7f889d8 100644 --- a/src/pretix/presale/views/locale.py +++ b/src/pretix/presale/views/locale.py @@ -11,7 +11,7 @@ from .robots import NoSearchIndexViewMixin class LocaleSet(NoSearchIndexViewMixin, View): def get(self, request, *args, **kwargs): - url = request.GET.get('next', request.META.get('HTTP_REFERER', '/')) + url = request.GET.get('next', request.headers.get('Referer', '/')) url = url if is_safe_url(url, allowed_hosts=[request.get_host()]) else '/' resp = HttpResponseRedirect(url) diff --git a/src/requirements/dev.txt b/src/requirements/dev.txt index 4dfadffea..2bc07a173 100644 --- a/src/requirements/dev.txt +++ b/src/requirements/dev.txt @@ -1,5 +1,5 @@ -django-debug-toolbar==1.9.1 -sqlparse==0.2.1 # pinned due to difficulties with django-debug-toolbar +django-debug-toolbar==1.11 +sqlparse==0.3.* # pinned due to difficulties with django-debug-toolbar # Testing requirements pycodestyle==2.5.* pyflakes==2.1.* @@ -7,15 +7,16 @@ pep8-naming flake8==3.7.* codecov coverage -pytest==3.6.* +pytest==4.4.* pytest-django -pytest-xdist==1.27.* isort -pytest-rerunfailures==4.* -pytest-mock==1.6.* -pytest-cache -pytest-sugar +pytest-rerunfailures==7.* +pytest-mock==1.10.* responses potypo freezegun +# Not really required, just nice to have +pytest-xdist==1.28.* +pytest-cache +pytest-sugar diff --git a/src/requirements/production.txt b/src/requirements/production.txt index 2f109447e..c791d9de9 100644 --- a/src/requirements/production.txt +++ b/src/requirements/production.txt @@ -1,19 +1,19 @@ # Functional requirements -Django>=2.1,<2.2 -djangorestframework==3.8.* -python-dateutil +Django==2.2.* +djangorestframework==3.9.* +python-dateutil==2.8.* pytz -django-bootstrap3==10.0.* +django-bootstrap3==11.0.* django-formset-js-improved==0.5.0.2 django-compressor==2.2.* django-hierarkey==1.0.*,>=1.0.3 -django-filter==2.0.* +django-filter==2.1.* reportlab==3.5.* PyPDF2==1.26.* Pillow==5.* django-libsass libsass -django-otp==0.4.* +django-otp==0.5.* python-u2flib-server==4.* django-formtools==2.1 celery==4.3.* @@ -28,7 +28,7 @@ dj-static csscompressor django-markup markdown<=2.2 -bleach==2.* +bleach==3.1.* sentry-sdk==0.7.* babel django-i18nfield>=1.4.0 diff --git a/src/setup.cfg b/src/setup.cfg index 5873633d9..bc3753c56 100644 --- a/src/setup.cfg +++ b/src/setup.cfg @@ -18,6 +18,9 @@ skip = make_testdata.py,wsgi.py,bootstrap,celery_app.py,pretix/settings.py,tests [tool:pytest] DJANGO_SETTINGS_MODULE=tests.settings addopts =--reruns 3 -rw +filterwarnings = + ignore:The 'warn' method is deprecated:DeprecationWarning + ignore:django.contrib.staticfiles.templatetags.static:DeprecationWarning [coverage:run] source = pretix diff --git a/src/setup.py b/src/setup.py index db1a09616..291b3a333 100644 --- a/src/setup.py +++ b/src/setup.py @@ -87,21 +87,21 @@ setup( keywords='tickets web shop ecommerce', install_requires=[ - 'Django>=2.1,<2.2', - 'djangorestframework==3.8.*', - 'python-dateutil==2.4.*', + 'Django==2.2.*', + 'djangorestframework==3.9.*', + 'python-dateutil==2.8.*', 'pytz', - 'django-bootstrap3==10.0.*', + 'django-bootstrap3==11.0.*', 'django-formset-js-improved==0.5.0.2', 'django-compressor==2.2.*', 'django-hierarkey==1.0.*,>=1.0.2', - 'django-filter==2.0.*', + 'django-filter==2.1.*', 'reportlab==3.5.*', 'Pillow==5.*', 'PyPDF2==1.26.*', 'django-libsass', 'libsass', - 'django-otp==0.4.*', + 'django-otp==0.5.*', 'python-u2flib-server==4.*', 'django-formtools==2.1', 'celery==4.3.*', @@ -116,7 +116,7 @@ setup( 'csscompressor', 'django-markup', 'markdown<=2.2', - 'bleach==2.*', + 'bleach==3.1.*', 'sentry-sdk==0.7.*', 'babel', 'paypalrestsdk==1.13.*', @@ -144,21 +144,22 @@ setup( ], extras_require={ 'dev': [ - 'django-debug-toolbar==1.9.1', - 'sqlparse==0.2.1', + 'django-debug-toolbar==1.11', + 'sqlparse==0.3.*', 'pycodestyle==2.5.*', 'pyflakes==2.1.*', 'flake8==3.7.*', 'pep8-naming', 'coveralls', 'coverage', - 'pytest==3.6.*', + 'pytest==4.4.*', 'pytest-django', - 'pytest-xdist==1.27.*', + 'pytest-xdist==1.28.*', 'isort', - 'pytest-mock==1.6.*', - 'pytest-rerunfailures', + 'pytest-mock==1.10.*', + 'pytest-rerunfailures==7.*', 'responses', + 'potypo', 'freezegun', ], 'memcached': ['pylibmc'], diff --git a/src/tests/api/test_cart.py b/src/tests/api/test_cart.py index e71eb6c13..7f07674e1 100644 --- a/src/tests/api/test_cart.py +++ b/src/tests/api/test_cart.py @@ -76,8 +76,8 @@ def test_cp_list(token_client, organizer, event, item, taxrule, question): cr = CartPosition.objects.create( event=event, cart_id="aaa", item=item, price=23, attendee_name_parts={'full_name': 'Peter'}, - datetime=datetime.datetime(2018, 6, 11, 10, 0, 0, 0), - expires=datetime.datetime(2018, 6, 11, 10, 0, 0, 0) + datetime=datetime.datetime(2018, 6, 11, 10, 0, 0, 0, tzinfo=UTC), + expires=datetime.datetime(2018, 6, 11, 10, 0, 0, 0, tzinfo=UTC) ) res = dict(TEST_CARTPOSITION_RES) res["id"] = cr.pk @@ -97,8 +97,8 @@ def test_cp_list_api(token_client, organizer, event, item, taxrule, question): cr = CartPosition.objects.create( event=event, cart_id="aaa@api", item=item, price=23, attendee_name_parts={'full_name': 'Peter'}, - datetime=datetime.datetime(2018, 6, 11, 10, 0, 0, 0), - expires=datetime.datetime(2018, 6, 11, 10, 0, 0, 0) + datetime=datetime.datetime(2018, 6, 11, 10, 0, 0, 0, tzinfo=UTC), + expires=datetime.datetime(2018, 6, 11, 10, 0, 0, 0, tzinfo=UTC) ) res = dict(TEST_CARTPOSITION_RES) res["id"] = cr.pk @@ -118,8 +118,8 @@ def test_cp_detail(token_client, organizer, event, item, taxrule, question): cr = CartPosition.objects.create( event=event, cart_id="aaa@api", item=item, price=23, attendee_name_parts={'full_name': 'Peter'}, - datetime=datetime.datetime(2018, 6, 11, 10, 0, 0, 0), - expires=datetime.datetime(2018, 6, 11, 10, 0, 0, 0) + datetime=datetime.datetime(2018, 6, 11, 10, 0, 0, 0, tzinfo=UTC), + expires=datetime.datetime(2018, 6, 11, 10, 0, 0, 0, tzinfo=UTC) ) res = dict(TEST_CARTPOSITION_RES) res["id"] = cr.pk @@ -139,8 +139,8 @@ def test_cp_delete(token_client, organizer, event, item, taxrule, question): cr = CartPosition.objects.create( event=event, cart_id="aaa@api", item=item, price=23, attendee_name_parts={'full_name': 'Peter'}, - datetime=datetime.datetime(2018, 6, 11, 10, 0, 0, 0), - expires=datetime.datetime(2018, 6, 11, 10, 0, 0, 0) + datetime=datetime.datetime(2018, 6, 11, 10, 0, 0, 0, tzinfo=UTC), + expires=datetime.datetime(2018, 6, 11, 10, 0, 0, 0, tzinfo=UTC) ) res = dict(TEST_CARTPOSITION_RES) res["id"] = cr.pk @@ -537,7 +537,7 @@ def test_cartpos_create_answer_validation(token_client, organizer, event, item, ) assert resp.status_code == 400 assert resp.data == { - 'answers': [{'non_field_errors': ['Date has wrong format. Use one of these formats instead: YYYY[-MM[-DD]].']}]} + 'answers': [{'non_field_errors': ['Date has wrong format. Use one of these formats instead: YYYY-MM-DD.']}]} question.type = Question.TYPE_DATETIME question.save() diff --git a/src/tests/api/test_items.py b/src/tests/api/test_items.py index e2e7fea1d..5bd4950ba 100644 --- a/src/tests/api/test_items.py +++ b/src/tests/api/test_items.py @@ -76,8 +76,8 @@ def cart_position(event, item, variations): c = CartPosition.objects.create( event=event, item=item, - datetime=datetime.now(), - expires=datetime.now() + timedelta(days=1), + datetime=testtime, + expires=testtime + timedelta(days=1), variation=variations[0], price=Decimal("23"), cart_id="z3fsn8jyufm5kpk768q69gkbyr5f4h6w" diff --git a/src/tests/api/test_orders.py b/src/tests/api/test_orders.py index 20d469f94..943db7f7d 100644 --- a/src/tests/api/test_orders.py +++ b/src/tests/api/test_orders.py @@ -2181,7 +2181,7 @@ def test_order_create_answer_validation(token_client, organizer, event, item, qu ) assert resp.status_code == 400 assert resp.data == {'positions': [{'answers': [ - {'non_field_errors': ['Date has wrong format. Use one of these formats instead: YYYY[-MM[-DD]].']}]}]} + {'non_field_errors': ['Date has wrong format. Use one of these formats instead: YYYY-MM-DD.']}]}]} question.type = Question.TYPE_DATETIME question.save() diff --git a/src/tests/base/test_models.py b/src/tests/base/test_models.py index cf6b4e554..94a2ad73b 100644 --- a/src/tests/base/test_models.py +++ b/src/tests/base/test_models.py @@ -1748,10 +1748,11 @@ class CachedFileTestCase(TestCase): cf.filename = "testfile.txt" cf.save() assert default_storage.exists(cf.file.name) + n = cf.file.name with default_storage.open(cf.file.name, 'r') as f: assert f.read().strip() == "file_content" cf.delete() - assert not default_storage.exists(cf.file.name) + assert not default_storage.exists(n) class CheckinListTestCase(TestCase): diff --git a/src/tests/control/test_events.py b/src/tests/control/test_events.py index b3bd75b08..8ea20d1c2 100644 --- a/src/tests/control/test_events.py +++ b/src/tests/control/test_events.py @@ -363,7 +363,7 @@ class EventsTest(SoupTest): def test_payment_settings_last_date_payment_after_presale_end(self): tr19 = self.event1.tax_rules.create(rate=Decimal('19.00')) - self.event1.presale_end = datetime.datetime.now() + self.event1.presale_end = now() self.event1.save(update_fields=['presale_end']) doc = self.post_doc('/control/event/%s/%s/settings/payment' % (self.orga1.slug, self.event1.slug), { 'payment_term_days': '2',