diff --git a/src/pretix/base/migrations/0045_auto_20161108_1542.py b/src/pretix/base/migrations/0045_auto_20161108_1542.py new file mode 100644 index 000000000..54c379709 --- /dev/null +++ b/src/pretix/base/migrations/0045_auto_20161108_1542.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.2 on 2016-11-08 15:42 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('pretixbase', '0044_auto_20161101_1610'), + ] + + operations = [ + migrations.AlterField( + model_name='cartposition', + name='expires', + field=models.DateTimeField(db_index=True, verbose_name='Expiration date'), + ), + migrations.AlterField( + model_name='voucher', + name='redeemed', + field=models.BooleanField(db_index=True, default=False, verbose_name='Redeemed'), + ), + migrations.AlterField( + model_name='voucher', + name='valid_until', + field=models.DateTimeField(blank=True, db_index=True, null=True, verbose_name='Valid until'), + ), + ] diff --git a/src/pretix/base/models/items.py b/src/pretix/base/models/items.py index a54ffdb9f..83cc3e623 100644 --- a/src/pretix/base/models/items.py +++ b/src/pretix/base/models/items.py @@ -582,7 +582,7 @@ class Quota(LoggedModel): Q(redeemed=False) & Q(Q(valid_until__isnull=True) | Q(valid_until__gte=now_dt)) & Q(Q(self._position_lookup) | Q(quota=self)) - ).distinct().count() + ).values('id').distinct().count() def count_in_cart(self, now_dt: datetime=None) -> int: from pretix.base.models import CartPosition @@ -595,21 +595,22 @@ class Quota(LoggedModel): & Q(Q(voucher__valid_until__isnull=True) | Q(voucher__valid_until__gte=now_dt)) ) & self._position_lookup - ).distinct().count() + ).values('id').distinct().count() def count_pending_orders(self) -> dict: from pretix.base.models import Order, OrderPosition + # This query has beeen benchmarked against a Count('id', distinct=True) aggregate and won by a small margin. return OrderPosition.objects.filter( self._position_lookup, order__status=Order.STATUS_PENDING, - ).distinct().count() + ).values('id').distinct().count() def count_paid_orders(self): from pretix.base.models import Order, OrderPosition return OrderPosition.objects.filter( self._position_lookup, order__status=Order.STATUS_PAID - ).distinct().count() + ).values('id').distinct().count() @cached_property def _position_lookup(self) -> Q: diff --git a/src/pretix/base/models/orders.py b/src/pretix/base/models/orders.py index e814bd033..103304c52 100644 --- a/src/pretix/base/models/orders.py +++ b/src/pretix/base/models/orders.py @@ -515,7 +515,8 @@ class CartPosition(AbstractPosition): auto_now_add=True ) expires = models.DateTimeField( - verbose_name=_("Expiration date") + verbose_name=_("Expiration date"), + db_index=True ) class Meta: diff --git a/src/pretix/base/models/vouchers.py b/src/pretix/base/models/vouchers.py index 10a8773a2..4da4aa601 100644 --- a/src/pretix/base/models/vouchers.py +++ b/src/pretix/base/models/vouchers.py @@ -70,10 +70,11 @@ class Voucher(LoggedModel): ) redeemed = models.BooleanField( verbose_name=_("Redeemed"), - default=False + default=False, + db_index=True ) valid_until = models.DateTimeField( - blank=True, null=True, + blank=True, null=True, db_index=True, verbose_name=_("Valid until") ) block_quota = models.BooleanField( diff --git a/src/pretix/base/payment.py b/src/pretix/base/payment.py index e0ded320e..38c2a80fa 100644 --- a/src/pretix/base/payment.py +++ b/src/pretix/base/payment.py @@ -4,7 +4,6 @@ from typing import Any, Dict from django import forms from django.contrib import messages -from django.db.models import Sum from django.dispatch import receiver from django.forms import Form from django.http import HttpRequest @@ -13,9 +12,10 @@ from django.utils.translation import ugettext_lazy as _ from pretix.base.decimal import round_decimal from pretix.base.i18n import I18nFormField, I18nTextarea, LazyI18nString -from pretix.base.models import CartPosition, Event, Order, Quota +from pretix.base.models import Event, Order, Quota from pretix.base.settings import SettingsSandbox from pretix.base.signals import register_payment_providers +from pretix.presale.views import get_cart_total class BasePaymentProvider: @@ -503,9 +503,7 @@ class FreeOrderProvider(BasePaymentProvider): messages.success(request, _('The order has been marked as refunded.')) def is_allowed(self, request: HttpRequest) -> bool: - return CartPosition.objects.filter( - cart_id=request.session.session_key, event=request.event - ).aggregate(sum=Sum('price'))['sum'] == 0 + return get_cart_total(request) == 0 def order_change_allowed(self, order: Order) -> bool: return False diff --git a/src/pretix/presale/checkoutflow.py b/src/pretix/presale/checkoutflow.py index 0163334a7..4c2dc78d3 100644 --- a/src/pretix/presale/checkoutflow.py +++ b/src/pretix/presale/checkoutflow.py @@ -2,7 +2,6 @@ from django.conf import settings from django.contrib import messages from django.core.exceptions import ValidationError from django.core.validators import EmailValidator -from django.db.models import Q, Sum from django.http import HttpResponseNotAllowed from django.shortcuts import redirect from django.utils import translation @@ -10,7 +9,7 @@ from django.utils.functional import cached_property from django.utils.translation import ugettext_lazy as _ from django.views.generic.base import TemplateResponseMixin -from pretix.base.models import CartPosition, Order +from pretix.base.models import Order from pretix.base.models.orders import InvoiceAddress from pretix.base.services.mail import SendMailException from pretix.base.services.orders import OrderError, perform_order @@ -18,7 +17,7 @@ from pretix.base.signals import register_payment_providers from pretix.multidomain.urlreverse import eventreverse from pretix.presale.forms.checkout import ContactForm, InvoiceAddressForm from pretix.presale.signals import checkout_flow_steps -from pretix.presale.views import CartMixin +from pretix.presale.views import CartMixin, get_cart_total from pretix.presale.views.async import AsyncAction from pretix.presale.views.questions import QuestionsViewMixin @@ -143,11 +142,12 @@ class QuestionsStep(QuestionsViewMixin, CartMixin, TemplateFlowStep): @cached_property def invoice_address(self): + iapk = self.request.session.get('invoice_address') + if not iapk: + return InvoiceAddress() + try: - return InvoiceAddress.objects.get( - pk=self.request.session.get('invoice_address'), - order__isnull=True - ) + return InvoiceAddress.objects.get(pk=iapk, order__isnull=True) except InvoiceAddress.DoesNotExist: return InvoiceAddress() @@ -218,9 +218,7 @@ class PaymentStep(QuestionsViewMixin, CartMixin, TemplateFlowStep): @cached_property def _total_order_value(self): - return CartPosition.objects.filter( - Q(cart_id=self.request.session.session_key) & Q(event=self.request.event) - ).aggregate(sum=Sum('price'))['sum'] + return get_cart_total(self.request) @cached_property def provider_forms(self): diff --git a/src/pretix/presale/views/__init__.py b/src/pretix/presale/views/__init__.py index 817e4bbe6..1d077c5aa 100644 --- a/src/pretix/presale/views/__init__.py +++ b/src/pretix/presale/views/__init__.py @@ -2,6 +2,7 @@ from datetime import timedelta from decimal import Decimal from itertools import groupby +from django.db.models import Sum from django.utils.functional import cached_property from django.utils.timezone import now @@ -106,6 +107,17 @@ def get_cart(request): return request._cart_cache +def get_cart_total(request): + if not hasattr(request, '_cart_total_cache'): + if hasattr(request, '_cart_cache'): + request._cart_total_cache = sum(i.price for i in request._cart_cache) + else: + request._cart_total_cache = CartPosition.objects.filter( + cart_id=request.session.session_key, event=request.event + ).aggregate(sum=Sum('price'))['sum'] + return request._cart_total_cache + + class EventViewMixin: def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) diff --git a/src/pretix/presale/views/questions.py b/src/pretix/presale/views/questions.py index 6e77c805e..5bb7ce3c7 100644 --- a/src/pretix/presale/views/questions.py +++ b/src/pretix/presale/views/questions.py @@ -3,6 +3,7 @@ from django.utils.functional import cached_property from pretix.base.models import CartPosition, OrderPosition, QuestionAnswer from pretix.presale.forms.checkout import QuestionsForm +from pretix.presale.views import get_cart class QuestionsViewMixin: @@ -14,7 +15,7 @@ class QuestionsViewMixin: submitted at once. """ formlist = [] - for cr in self.positions: + for cr in get_cart(self.request): cartpos = cr if isinstance(cr, CartPosition) else None orderpos = cr if isinstance(cr, OrderPosition) else None form = QuestionsForm(event=self.request.event,