From feb7f419d36d568ad07c94010648a72bb8305271 Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Wed, 6 May 2020 16:19:46 +0200 Subject: [PATCH] Minor performance improvements for generating many subevents --- src/pretix/base/models/event.py | 6 +- src/pretix/control/views/subevents.py | 154 ++++++++++++++++---------- 2 files changed, 102 insertions(+), 58 deletions(-) diff --git a/src/pretix/base/models/event.py b/src/pretix/base/models/event.py index 133459aec4..5ef890e081 100644 --- a/src/pretix/base/models/event.py +++ b/src/pretix/base/models/event.py @@ -1115,13 +1115,15 @@ class SubEvent(EventMixin, LoggedModel): return not self.orderposition_set.exists() def delete(self, *args, **kwargs): + clear_cache = kwargs.pop('clear_cache', False) super().delete(*args, **kwargs) - if self.event: + if self.event and clear_cache: self.event.cache.clear() def save(self, *args, **kwargs): + clear_cache = kwargs.pop('clear_cache', False) super().save(*args, **kwargs) - if self.event: + if self.event and clear_cache: self.event.cache.clear() @staticmethod diff --git a/src/pretix/control/views/subevents.py b/src/pretix/control/views/subevents.py index 7a66105605..1e04fbbcc2 100644 --- a/src/pretix/control/views/subevents.py +++ b/src/pretix/control/views/subevents.py @@ -17,7 +17,7 @@ from django.utils.translation import gettext_lazy as _, pgettext_lazy from django.views import View from django.views.generic import CreateView, DeleteView, ListView, UpdateView -from pretix.base.models import CartPosition +from pretix.base.models import CartPosition, LogEntry from pretix.base.models.checkin import CheckinList from pretix.base.models.event import SubEvent, SubEventMetaValue from pretix.base.models.items import ( @@ -667,7 +667,7 @@ class SubEventBulkCreate(SubEventEditorMixin, EventPermissionRequiredMixin, Crea def get_times(self): times = [] for f in self.time_formset: - if f in self.time_formset.deleted_forms: + if f in self.time_formset.deleted_forms or not f.cleaned_data.get('time_from'): continue times.append(f.cleaned_data) return times @@ -719,7 +719,7 @@ class SubEventBulkCreate(SubEventEditorMixin, EventPermissionRequiredMixin, Crea @transaction.atomic def form_valid(self, form): tz = self.request.event.timezone - cnt = 0 + subevents = [] for rdate in self.get_rrule_set(): for t in self.get_times(): se = copy.copy(form.instance) @@ -745,69 +745,111 @@ class SubEventBulkCreate(SubEventEditorMixin, EventPermissionRequiredMixin, Crea if form.cleaned_data.get('rel_presale_end') else None ) - se.save() - data = dict(form.cleaned_data) - for f in self.plugin_forms: - data.update({ - k: (f.cleaned_data.get(k).name - if isinstance(f.cleaned_data.get(k), File) - else f.cleaned_data.get(k)) - for k in f.cleaned_data - }) - se.log_action('pretix.subevent.added', data=data, user=self.request.user) + se.save(clear_cache=False) + subevents.append(se) - for f in self.meta_forms: - if f.cleaned_data.get('value'): - i = copy.copy(f.instance) - i.subevent = se - i.save() + data = dict(form.cleaned_data) + for f in self.plugin_forms: + data.update({ + k: (f.cleaned_data.get(k).name + if isinstance(f.cleaned_data.get(k), File) + else f.cleaned_data.get(k)) + for k in f.cleaned_data + }) + log_entries = [] + for se in subevents: + log_entries.append(se.log_action('pretix.subevent.added', data=data, user=self.request.user, save=False)) - for f in self.formset.forms: - if self.formset._should_delete_form(f): - continue + to_save = [] + for f in self.meta_forms: + if f.cleaned_data.get('value'): + for se in subevents: i = copy.copy(f.instance) + i.pk = None i.subevent = se - i.event = se.event - i.save() - selected_items = set(list(self.request.event.items.filter(id__in=[ - i.split('-')[0] for i in f.cleaned_data.get('itemvars', []) - ]))) - selected_variations = list(ItemVariation.objects.filter(item__event=self.request.event, id__in=[ - i.split('-')[1] for i in f.cleaned_data.get('itemvars', []) if '-' in i - ])) - i.items.add(*[_i for _i in selected_items]) - i.variations.add(*[_i for _i in selected_variations]) + to_save.append(i) + SubEventMetaValue.objects.bulk_create(to_save) - change_data = {k: f.cleaned_data.get(k) for k in f.changed_data} - change_data['id'] = i.pk - i.log_action(action='pretix.event.quota.added', user=self.request.user, data=change_data) - se.log_action('pretix.subevent.quota.added', user=self.request.user, data=change_data) + to_save_items = [] + to_save_variations = [] + for f in self.itemvar_forms: + for se in subevents: + i = copy.copy(f.instance) + i.pk = None + i.subevent = se + if isinstance(i, SubEventItem): + to_save_items.append(i) + else: + to_save_variations.append(i) + SubEventItem.objects.bulk_create(to_save_items) + SubEventItemVariation.objects.bulk_create(to_save_variations) - for f in self.cl_formset.forms: - if self.cl_formset._should_delete_form(f): - continue - i = copy.copy(f.instance) - i.subevent = se - i.event = se.event - i.save() - i.limit_products.add(*f.cleaned_data.get('limit_products', [])) - change_data = {k: f.cleaned_data.get(k) for k in f.changed_data} - change_data['id'] = i.pk - i.log_action(action='pretix.event.checkinlist.added', user=self.request.user, data=change_data) + to_save_items = [] + to_save_variations = [] + for f in self.formset.forms: + if self.formset._should_delete_form(f): + continue - for f in self.itemvar_forms: - i = copy.copy(f.instance) - i.subevent = se - i.save() + change_data = {k: f.cleaned_data.get(k) for k in f.changed_data} + for se in subevents: + i = copy.copy(f.instance) + i.pk = None + i.subevent = se + i.event = se.event + i.save(clear_cache=False) + selected_items = set(list(self.request.event.items.filter(id__in=[ + i.split('-')[0] for i in f.cleaned_data.get('itemvars', []) + ]))) + selected_variations = list(ItemVariation.objects.filter(item__event=self.request.event, id__in=[ + i.split('-')[1] for i in f.cleaned_data.get('itemvars', []) if '-' in i + ])) + for _i in selected_items: + to_save_items.append(Quota.items.through(quota_id=i.pk, item_id=_i.pk)) + for _i in selected_variations: + to_save_variations.append(Quota.variations.through(quota_id=i.pk, itemvariation_id=_i.pk)) - for f in self.plugin_forms: - f.is_valid() - f.subevent = se - f.save() + change_data['id'] = i.pk + log_entries.append( + i.log_action(action='pretix.event.quota.added', user=self.request.user, + data=change_data, save=False) + ) + log_entries.append( + se.log_action('pretix.subevent.quota.added', user=self.request.user, data=change_data, save=False) + ) + Quota.items.through.objects.bulk_create(to_save_items) + Quota.variations.through.objects.bulk_create(to_save_variations) - cnt += 1 + to_save_products = [] + for f in self.cl_formset.forms: + if self.cl_formset._should_delete_form(f): + continue + change_data = {k: f.cleaned_data.get(k) for k in f.changed_data} + for se in subevents: + i = copy.copy(f.instance) + i.subevent = se + i.event = se.event + i.save() + for _i in f.cleaned_data.get('limit_products', []): + to_save_products.append(CheckinList.limit_products.through(checkinlist_id=i.pk, item_id=_i.pk)) - messages.success(self.request, pgettext_lazy('subevent', '{} new dates have been created.').format(cnt)) + change_data['id'] = i.pk + log_entries.append( + i.log_action(action='pretix.event.checkinlist.added', user=self.request.user, data=change_data, + save=False) + ) + CheckinList.limit_products.through.objects.bulk_create(to_save_products) + + for f in self.plugin_forms: + f.is_valid() + for se in subevents: + f.subevent = se + f.save() + + LogEntry.objects.bulk_create(log_entries) + + self.request.event.cache.clear() + messages.success(self.request, pgettext_lazy('subevent', '{} new dates have been created.').format(len(subevents))) + return self.get(self.request, *self.args, **self.kwargs) return redirect(reverse('control:event.subevents', kwargs={ 'organizer': self.request.event.organizer.slug, 'event': self.request.event.slug,