forked from CGM_Public/pretix_original
Re-structure some querying on cart and order pages to reduce load
This commit is contained in:
@@ -902,7 +902,7 @@ class Order(LockModel, LoggedModel):
|
||||
|
||||
@property
|
||||
def positions_with_tickets(self):
|
||||
for op in self.positions.all():
|
||||
for op in self.positions.select_related('item'):
|
||||
if not op.generate_ticket:
|
||||
continue
|
||||
yield op
|
||||
@@ -1155,7 +1155,7 @@ class AbstractPosition(models.Model):
|
||||
(2) questions: a list of Question objects, extended by an 'answer' property
|
||||
"""
|
||||
self.answ = {}
|
||||
for a in self.answers.all():
|
||||
for a in getattr(self, 'answerlist', self.answers.all()): # use prefetch_related cache from get_cart
|
||||
self.answ[a.question_id] = a
|
||||
|
||||
# We need to clone our question objects, otherwise we will override the cached
|
||||
|
||||
@@ -673,7 +673,7 @@ class QuestionsStep(QuestionsViewMixin, CartMixin, TemplateFlowStep):
|
||||
return ctx
|
||||
|
||||
|
||||
class PaymentStep(QuestionsViewMixin, CartMixin, TemplateFlowStep):
|
||||
class PaymentStep(CartMixin, TemplateFlowStep):
|
||||
priority = 200
|
||||
identifier = "payment"
|
||||
template_name = "pretixpresale/event/checkout_payment.html"
|
||||
|
||||
@@ -313,7 +313,7 @@
|
||||
{% endif %}
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
{% if order.user_change_allowed or order.user_cancel_allowed %}
|
||||
{% if user_change_allowed or user_cancel_allowed %}
|
||||
<div class="panel panel-primary panel-cancellation">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">
|
||||
@@ -321,7 +321,7 @@
|
||||
</h3>
|
||||
</div>
|
||||
<ul class="list-group">
|
||||
{% if order.user_change_allowed %}
|
||||
{% if user_change_allowed %}
|
||||
<li class="list-group-item">
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
@@ -335,7 +335,7 @@
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if order.user_cancel_allowed %}
|
||||
{% if user_cancel_allowed %}
|
||||
<li class="list-group-item">
|
||||
{% if order.status == "p" and order.total != 0 %}
|
||||
{% if order.user_cancel_fee >= order.total %}
|
||||
|
||||
@@ -5,14 +5,14 @@ from functools import wraps
|
||||
from itertools import groupby
|
||||
|
||||
from django.conf import settings
|
||||
from django.db.models import Prefetch, Sum
|
||||
from django.db.models import Prefetch, Sum, Exists, OuterRef
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.timezone import now
|
||||
from django_scopes import scopes_disabled
|
||||
|
||||
from pretix.base.i18n import language
|
||||
from pretix.base.models import (
|
||||
CartPosition, InvoiceAddress, OrderPosition, QuestionAnswer,
|
||||
CartPosition, InvoiceAddress, OrderPosition, QuestionAnswer, ItemAddOn, Question, QuestionOption,
|
||||
)
|
||||
from pretix.base.services.cart import get_fees
|
||||
from pretix.helpers.cookies import set_cookie_without_samesite
|
||||
@@ -68,12 +68,15 @@ class CartMixin:
|
||||
prefetch.append(Prefetch('answers', queryset=QuestionAnswer.objects.prefetch_related('options')))
|
||||
|
||||
cartpos = queryset.order_by(
|
||||
'item__category__position', 'item__category_id', 'item__position', 'item__name', 'variation__value'
|
||||
'item__category__position', 'item__category_id', 'item__position', 'item__name',
|
||||
'variation__value'
|
||||
).select_related(
|
||||
'item', 'variation', 'addon_to', 'subevent', 'subevent__event', 'subevent__event__organizer', 'seat'
|
||||
'item', 'variation', 'addon_to', 'subevent', 'subevent__event',
|
||||
'subevent__event__organizer', 'seat'
|
||||
).prefetch_related(
|
||||
*prefetch
|
||||
)
|
||||
|
||||
else:
|
||||
cartpos = self.positions
|
||||
|
||||
@@ -123,7 +126,7 @@ class CartMixin:
|
||||
or pos.pk in has_addons \
|
||||
or pos.addon_to_id \
|
||||
or pos.item.issue_giftcard \
|
||||
or (answers and (has_attendee_data or pos.item.questions.exists())):
|
||||
or (answers and (has_attendee_data or bool(pos.item.questions.all()))): # do not use .exists() to re-use prefetch cache
|
||||
return (
|
||||
# standalone positions are grouped by main product position id, addons below them also sorted by position id
|
||||
i, addon_penalty, pos.pk,
|
||||
@@ -147,7 +150,8 @@ class CartMixin:
|
||||
group.total = group.count * group.price
|
||||
group.net_total = group.count * group.net_price
|
||||
group.has_questions = answers and k[0] != ""
|
||||
group.tax_rule = group.item.tax_rule
|
||||
if not hasattr(group, 'tax_rule'):
|
||||
group.tax_rule = group.item.tax_rule
|
||||
|
||||
group.bundle_sum = group.price + sum(a.price for a in has_addons[group.pk])
|
||||
group.bundle_sum_net = group.net_price + sum(a.net_price for a in has_addons[group.pk])
|
||||
@@ -214,6 +218,8 @@ def cart_exists(request):
|
||||
|
||||
def get_cart(request):
|
||||
from pretix.presale.views.cart import get_or_create_cart_id
|
||||
qqs = request.event.questions.all()
|
||||
qqs = qqs.filter(ask_during_checkin=False, hidden=False)
|
||||
|
||||
if not hasattr(request, '_cart_cache'):
|
||||
cart_id = get_or_create_cart_id(request, create=False)
|
||||
@@ -222,11 +228,36 @@ def get_cart(request):
|
||||
else:
|
||||
request._cart_cache = CartPosition.objects.filter(
|
||||
cart_id=cart_id, event=request.event
|
||||
).annotate(
|
||||
has_addon_choices=Exists(
|
||||
ItemAddOn.objects.filter(
|
||||
base_item_id=OuterRef('item_id')
|
||||
)
|
||||
)
|
||||
).order_by(
|
||||
'item', 'variation'
|
||||
'item__category__position', 'item__category_id', 'item__position', 'item__name', 'variation__value'
|
||||
).select_related(
|
||||
'item', 'variation', 'subevent', 'subevent__event', 'subevent__event__organizer',
|
||||
'item__tax_rule', 'addon_to'
|
||||
).select_related(
|
||||
'addon_to'
|
||||
).prefetch_related(
|
||||
'addons', 'addons__item', 'addons__variation',
|
||||
Prefetch('answers',
|
||||
QuestionAnswer.objects.prefetch_related('options'),
|
||||
to_attr='answerlist'),
|
||||
Prefetch('item__questions',
|
||||
qqs.prefetch_related(
|
||||
Prefetch('options', QuestionOption.objects.prefetch_related(Prefetch(
|
||||
# This prefetch statement is utter bullshit, but it actually prevents Django from doing
|
||||
# a lot of queries since ModelChoiceIterator stops trying to be clever once we have
|
||||
# a prefetch lookup on this query...
|
||||
'question',
|
||||
Question.objects.none(),
|
||||
to_attr='dummy'
|
||||
)))
|
||||
).select_related('dependency_question'),
|
||||
to_attr='questions_to_ask')
|
||||
)
|
||||
for cp in request._cart_cache:
|
||||
cp.event = request.event # Populate field with known value to save queries
|
||||
|
||||
@@ -408,7 +408,7 @@ class EventIndex(EventViewMixin, EventListMixin, CartMixin, TemplateView):
|
||||
context['ev'] = self.subevent or self.request.event
|
||||
context['subevent'] = self.subevent
|
||||
context['cart'] = self.get_cart()
|
||||
context['has_addon_choices'] = get_cart(self.request).filter(item__addons__isnull=False).exists()
|
||||
context['has_addon_choices'] = any(cp.has_addon_choices for cp in get_cart(self.request))\
|
||||
|
||||
if self.subevent:
|
||||
context['frontpage_text'] = str(self.subevent.frontpage_text)
|
||||
|
||||
@@ -245,6 +245,8 @@ class OrderDetails(EventViewMixin, OrderDetailMixin, CartMixin, TicketPageMixin,
|
||||
).exclude(
|
||||
provider__in=('offsetting', 'reseller', 'boxoffice', 'manual')
|
||||
)
|
||||
ctx['user_change_allowed'] = self.order.user_change_allowed
|
||||
ctx['user_cancel_allowed'] = self.order.user_cancel_allowed
|
||||
for r in ctx['refunds']:
|
||||
if r.provider == 'giftcard':
|
||||
gc = GiftCard.objects.get(pk=r.info_data.get('gift_card'))
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
from django.db.models import Prefetch
|
||||
from django.utils.functional import cached_property
|
||||
|
||||
from pretix.base.models import Question, QuestionAnswer, QuestionOption
|
||||
from pretix.base.views.mixins import BaseQuestionsViewMixin
|
||||
from pretix.presale.forms.checkout import QuestionsForm
|
||||
from pretix.presale.views import get_cart
|
||||
@@ -13,27 +11,5 @@ class QuestionsViewMixin(BaseQuestionsViewMixin):
|
||||
|
||||
@cached_property
|
||||
def _positions_for_questions(self):
|
||||
qqs = self.request.event.questions.all()
|
||||
if self.only_user_visible:
|
||||
qqs = qqs.filter(ask_during_checkin=False, hidden=False)
|
||||
cart = get_cart(self.request).select_related(
|
||||
'addon_to'
|
||||
).prefetch_related(
|
||||
'addons', 'addons__item', 'addons__variation',
|
||||
Prefetch('answers',
|
||||
QuestionAnswer.objects.prefetch_related('options'),
|
||||
to_attr='answerlist'),
|
||||
Prefetch('item__questions',
|
||||
qqs.prefetch_related(
|
||||
Prefetch('options', QuestionOption.objects.prefetch_related(Prefetch(
|
||||
# This prefetch statement is utter bullshit, but it actually prevents Django from doing
|
||||
# a lot of queries since ModelChoiceIterator stops trying to be clever once we have
|
||||
# a prefetch lookup on this query...
|
||||
'question',
|
||||
Question.objects.none(),
|
||||
to_attr='dummy'
|
||||
)))
|
||||
).select_related('dependency_question'),
|
||||
to_attr='questions_to_ask')
|
||||
)
|
||||
cart = get_cart(self.request)
|
||||
return sorted(list(cart), key=self._keyfunc)
|
||||
|
||||
Reference in New Issue
Block a user