diff --git a/src/pretix/base/services/invoices.py b/src/pretix/base/services/invoices.py index da6ed1b780..c5f188a412 100644 --- a/src/pretix/base/services/invoices.py +++ b/src/pretix/base/services/invoices.py @@ -1,4 +1,3 @@ -import copy import json import logging import urllib.error @@ -27,6 +26,7 @@ from pretix.base.settings import GlobalSettingsObject from pretix.base.signals import periodic_task from pretix.celery_app import app from pretix.helpers.database import rolledback_transaction +from pretix.helpers.models import modelcopy logger = logging.getLogger(__name__) @@ -171,7 +171,7 @@ def build_cancellation(invoice: Invoice): def generate_cancellation(invoice: Invoice, trigger_pdf=True): - cancellation = copy.deepcopy(invoice) + cancellation = modelcopy(invoice) cancellation.pk = None cancellation.invoice_no = None cancellation.prefix = None diff --git a/src/pretix/base/services/orders.py b/src/pretix/base/services/orders.py index cbaa646a89..63b9ca792c 100644 --- a/src/pretix/base/services/orders.py +++ b/src/pretix/base/services/orders.py @@ -1,4 +1,3 @@ -import copy import json import logging from collections import Counter, namedtuple @@ -44,6 +43,7 @@ from pretix.base.signals import ( allow_ticket_download, order_fee_calculation, order_placed, periodic_task, ) from pretix.celery_app import app +from pretix.helpers.models import modelcopy from pretix.multidomain.urlreverse import build_absolute_uri error_messages = { @@ -923,7 +923,7 @@ class OrderChangeManager: op.save() try: - ia = copy.deepcopy(self.order.invoice_address) + ia = modelcopy(self.order.invoice_address) ia.pk = None ia.order = split_order ia.save() @@ -947,7 +947,7 @@ class OrderChangeManager: split_order.total += fee.value for fee in self.order.fees.exclude(fee_type=OrderFee.FEE_TYPE_PAYMENT): - new_fee = copy.deepcopy(fee) + new_fee = modelcopy(fee) new_fee.pk = None new_fee.order = split_order split_order.total += new_fee.value diff --git a/src/pretix/control/forms/item.py b/src/pretix/control/forms/item.py index f8438aeaf7..298702e343 100644 --- a/src/pretix/control/forms/item.py +++ b/src/pretix/control/forms/item.py @@ -1,5 +1,3 @@ -import copy - from django import forms from django.core.exceptions import ValidationError from django.db.models import Max @@ -18,6 +16,7 @@ from pretix.base.models.items import ItemAddOn from pretix.base.signals import item_copy_data from pretix.control.forms import SplitDateTimePickerWidget from pretix.control.forms.widgets import Select2 +from pretix.helpers.models import modelcopy from pretix.helpers.money import change_decimal_field @@ -78,7 +77,7 @@ class QuotaForm(I18nModelForm): self.instance = kwargs.get('instance', None) self.event = kwargs.get('event') items = kwargs.pop('items', None) or self.event.items.prefetch_related('variations') - self.original_instance = copy.deepcopy(self.instance) if self.instance else None + self.original_instance = modelcopy(self.instance) if self.instance else None initial = kwargs.get('initial', {}) if self.instance and self.instance.pk: initial['itemvars'] = [str(i.pk) for i in self.instance.items.all()] + [ diff --git a/src/pretix/control/forms/vouchers.py b/src/pretix/control/forms/vouchers.py index e231f51115..48bd1cb651 100644 --- a/src/pretix/control/forms/vouchers.py +++ b/src/pretix/control/forms/vouchers.py @@ -1,5 +1,3 @@ -import copy - from django import forms from django.core.exceptions import ObjectDoesNotExist, ValidationError from django.db.models.functions import Lower @@ -11,6 +9,7 @@ from pretix.base.models import Item, Voucher from pretix.control.forms import SplitDateTimePickerWidget from pretix.control.forms.widgets import Select2, Select2ItemVarQuota from pretix.control.signals import voucher_form_validation +from pretix.helpers.models import modelcopy class FakeChoiceField(forms.ChoiceField): @@ -45,7 +44,7 @@ class VoucherForm(I18nModelForm): instance = kwargs.get('instance') initial = kwargs.get('initial') if instance: - self.initial_instance_data = copy.deepcopy(instance) + self.initial_instance_data = modelcopy(instance) try: if instance.variation: initial['itemvar'] = '%d-%d' % (instance.item.pk, instance.variation.pk) @@ -217,7 +216,7 @@ class VoucherBulkForm(VoucherForm): def save(self, event, *args, **kwargs): objs = [] for code in self.cleaned_data['codes']: - obj = copy.deepcopy(self.instance) + obj = modelcopy(self.instance) obj.event = event obj.code = code data = dict(self.cleaned_data) diff --git a/src/pretix/control/views/subevents.py b/src/pretix/control/views/subevents.py index be98112d42..00a784da4e 100644 --- a/src/pretix/control/views/subevents.py +++ b/src/pretix/control/views/subevents.py @@ -33,6 +33,7 @@ from pretix.control.forms.subevents import ( from pretix.control.permissions import EventPermissionRequiredMixin from pretix.control.views import PaginationMixin from pretix.control.views.event import MetaDataEditorMixin +from pretix.helpers.models import modelcopy class SubEventList(EventPermissionRequiredMixin, PaginationMixin, ListView): @@ -424,7 +425,7 @@ class SubEventCreate(SubEventEditorMixin, EventPermissionRequiredMixin, CreateVi kwargs = super().get_form_kwargs() kwargs['event'] = self.request.event if self.copy_from: - i = copy.deepcopy(self.copy_from) + i = modelcopy(self.copy_from) i.pk = None kwargs['instance'] = i else: diff --git a/src/pretix/helpers/models.py b/src/pretix/helpers/models.py index 6aaaed8ab9..48c8e86597 100644 --- a/src/pretix/helpers/models.py +++ b/src/pretix/helpers/models.py @@ -1,3 +1,5 @@ +import copy + from django.db import models @@ -8,3 +10,14 @@ class Thumbnail(models.Model): class Meta: unique_together = (('source', 'size'),) + + +def modelcopy(obj: models.Model): + n = copy.copy(obj) + for f in obj._meta.fields: + val = getattr(obj, f.name) + if isinstance(val, models.Model): + setattr(n, f.name, val) + else: + setattr(n, f.name, copy.deepcopy(val)) + return n diff --git a/src/tests/control/test_vouchers.py b/src/tests/control/test_vouchers.py index d9cf9f524b..60908a1667 100644 --- a/src/tests/control/test_vouchers.py +++ b/src/tests/control/test_vouchers.py @@ -394,6 +394,12 @@ class VoucherFormTest(SoupTest): 'itemvar': '%d' % self.shirt.pk, }) + def test_create_bulk_many(self): + self._create_bulk_vouchers({ + 'codes': 'ABCDE\nDEFGH\nIJKLM\nNOPQR\nSTUVW\nXYZ', + 'itemvar': '%d' % self.ticket.pk, + }) + def test_create_blocking_bulk_quota_full(self): self.quota_tickets.size = 0 self.quota_tickets.save()