diff --git a/src/pretix/api/serializers/event.py b/src/pretix/api/serializers/event.py index 4236c06a69..d0491de384 100644 --- a/src/pretix/api/serializers/event.py +++ b/src/pretix/api/serializers/event.py @@ -629,9 +629,13 @@ class EventSettingsSerializer(serializers.Serializer): super().__init__(*args, **kwargs) for fname in self.default_fields: kwargs = DEFAULTS[fname].get('serializer_kwargs', {}) + if callable(kwargs): + kwargs = kwargs() kwargs.setdefault('required', False) kwargs.setdefault('allow_null', True) form_kwargs = DEFAULTS[fname].get('form_kwargs', {}) + if callable(form_kwargs): + form_kwargs = form_kwargs() if 'serializer_class' not in DEFAULTS[fname]: raise ValidationError('{} has no serializer class'.format(fname)) f = DEFAULTS[fname]['serializer_class']( diff --git a/src/pretix/base/forms/__init__.py b/src/pretix/base/forms/__init__.py index 378a55aedd..160cb93ae0 100644 --- a/src/pretix/base/forms/__init__.py +++ b/src/pretix/base/forms/__init__.py @@ -65,6 +65,8 @@ class SettingsForm(i18nfield.forms.I18nFormMixin, HierarkeyForm): super().__init__(*args, **kwargs) for fname in self.auto_fields: kwargs = DEFAULTS[fname].get('form_kwargs', {}) + if callable(kwargs): + kwargs = kwargs() kwargs.setdefault('required', False) field = DEFAULTS[fname]['form_class']( **kwargs diff --git a/src/pretix/base/models/invoices.py b/src/pretix/base/models/invoices.py index dced6a2d81..e617dc8d05 100644 --- a/src/pretix/base/models/invoices.py +++ b/src/pretix/base/models/invoices.py @@ -9,10 +9,10 @@ from django.utils import timezone from django.utils.crypto import get_random_string from django.utils.functional import cached_property from django.utils.translation import pgettext -from django_countries.fields import CountryField from django_scopes import ScopedManager from pretix.base.settings import COUNTRIES_WITH_STATE_IN_ADDRESS +from pretix.helpers.countries import FastCountryField def invoice_filename(instance, filename: str) -> str: @@ -84,7 +84,7 @@ class Invoice(models.Model): invoice_from_name = models.CharField(max_length=190, null=True) invoice_from_zipcode = models.CharField(max_length=190, null=True) invoice_from_city = models.CharField(max_length=190, null=True) - invoice_from_country = CountryField(null=True) + invoice_from_country = FastCountryField(null=True) invoice_from_tax_id = models.CharField(max_length=190, null=True) invoice_from_vat_id = models.CharField(max_length=190, null=True) invoice_to = models.TextField() @@ -94,7 +94,7 @@ class Invoice(models.Model): invoice_to_zipcode = models.CharField(max_length=190, null=True) invoice_to_city = models.TextField(null=True) invoice_to_state = models.CharField(max_length=190, null=True) - invoice_to_country = CountryField(null=True) + invoice_to_country = FastCountryField(null=True) invoice_to_vat_id = models.TextField(null=True) invoice_to_beneficiary = models.TextField(null=True) date = models.DateField(default=today) diff --git a/src/pretix/base/models/orders.py b/src/pretix/base/models/orders.py index 4370b781c2..ae365f396e 100644 --- a/src/pretix/base/models/orders.py +++ b/src/pretix/base/models/orders.py @@ -27,7 +27,7 @@ from django.utils.formats import date_format from django.utils.functional import cached_property from django.utils.timezone import make_aware, now from django.utils.translation import gettext_lazy as _, pgettext_lazy -from django_countries.fields import Country, CountryField +from django_countries.fields import Country from django_scopes import ScopedManager, scopes_disabled from i18nfield.strings import LazyI18nString from jsonfallback.fields import FallbackJSONField @@ -44,7 +44,7 @@ from pretix.base.services.locking import NoLockManager from pretix.base.settings import PERSON_NAME_SCHEMES from pretix.base.signals import order_gracefully_delete -from ...helpers.countries import CachedCountries +from ...helpers.countries import CachedCountries, FastCountryField from .base import LockModel, LoggedModel from .event import Event, SubEvent from .items import Item, ItemVariation, Question, QuestionOption, Quota @@ -1076,7 +1076,7 @@ class AbstractPosition(models.Model): street = models.TextField(verbose_name=_('Address'), blank=True, null=True) zipcode = models.CharField(max_length=30, verbose_name=_('ZIP code'), blank=True, null=True) city = models.CharField(max_length=255, verbose_name=_('City'), blank=True, null=True) - country = CountryField(verbose_name=_('Country'), blank=True, blank_label=_('Select country'), null=True) + country = FastCountryField(verbose_name=_('Country'), blank=True, blank_label=_('Select country'), null=True) state = models.CharField(max_length=255, verbose_name=pgettext_lazy('address', 'State'), blank=True, null=True) class Meta: @@ -2150,8 +2150,8 @@ class InvoiceAddress(models.Model): zipcode = models.CharField(max_length=30, verbose_name=_('ZIP code'), blank=False) city = models.CharField(max_length=255, verbose_name=_('City'), blank=False) country_old = models.CharField(max_length=255, verbose_name=_('Country'), blank=False) - country = CountryField(verbose_name=_('Country'), blank=False, blank_label=_('Select country'), - countries=CachedCountries) + country = FastCountryField(verbose_name=_('Country'), blank=False, blank_label=_('Select country'), + countries=CachedCountries) state = models.CharField(max_length=255, verbose_name=pgettext_lazy('address', 'State'), blank=True) vat_id = models.CharField(max_length=255, blank=True, verbose_name=_('VAT ID'), help_text=_('Only for business customers within the EU.')) diff --git a/src/pretix/base/models/tax.py b/src/pretix/base/models/tax.py index 66fe8d5daa..bc288097e6 100644 --- a/src/pretix/base/models/tax.py +++ b/src/pretix/base/models/tax.py @@ -5,12 +5,12 @@ from django.core.exceptions import ValidationError from django.db import models from django.utils.formats import localize from django.utils.translation import gettext_lazy as _ -from django_countries.fields import CountryField from i18nfield.fields import I18nCharField from pretix.base.decimal import round_decimal from pretix.base.models.base import LoggedModel from pretix.base.templatetags.money import money_filter +from pretix.helpers.countries import FastCountryField class TaxedPrice: @@ -116,7 +116,7 @@ class TaxRule(LoggedModel): "ID. Only enable this option after consulting a tax counsel. No warranty given for correct tax " "calculation. USE AT YOUR OWN RISK.") ) - home_country = CountryField( + home_country = FastCountryField( verbose_name=_('Merchant country'), blank=True, help_text=_('Your country of residence. This is the country the EU reverse charge rule will not apply in, ' diff --git a/src/pretix/base/settings.py b/src/pretix/base/settings.py index 258ae58ac7..0c9bd79844 100644 --- a/src/pretix/base/settings.py +++ b/src/pretix/base/settings.py @@ -13,7 +13,6 @@ from django.db.models import Model from django.utils.translation import ( gettext_lazy as _, gettext_noop, pgettext, pgettext_lazy, ) -from django_countries import countries from hierarkey.models import GlobalSettingsBase, Hierarkey from i18nfield.forms import I18nFormField, I18nTextarea, I18nTextInput from i18nfield.strings import LazyI18nString @@ -27,9 +26,15 @@ from pretix.base.reldate import ( SerializerRelativeDateField, SerializerRelativeDateTimeField, ) from pretix.control.forms import MultipleLanguagesWidget, SingleLanguageWidget +from pretix.helpers.countries import CachedCountries -allcountries = list(countries) -allcountries.insert(0, ('', _('Select country'))) + +def country_choice_kwargs(): + allcountries = list(CachedCountries()) + allcountries.insert(0, ('', _('Select country'))) + return { + 'choices': allcountries + } DEFAULTS = { @@ -570,13 +575,8 @@ DEFAULTS = { 'type': str, 'form_class': forms.ChoiceField, 'serializer_class': serializers.ChoiceField, - 'serializer_kwargs': dict( - choices=allcountries, - ), - 'form_kwargs': dict( - choices=allcountries, - label=_("Country"), - ) + 'serializer_kwargs': country_choice_kwargs, + 'form_kwargs': country_choice_kwargs, }, 'invoice_address_from_tax_id': { 'default': '', diff --git a/src/pretix/helpers/countries.py b/src/pretix/helpers/countries.py index c23adce5bd..0fdfc9c152 100644 --- a/src/pretix/helpers/countries.py +++ b/src/pretix/helpers/countries.py @@ -1,6 +1,7 @@ from django.core.cache import cache from django.utils.translation import get_language from django_countries import Countries +from django_countries.fields import CountryField class CachedCountries(Countries): @@ -27,3 +28,24 @@ class CachedCountries(Countries): self._cached_lists[cache_key] = val cache.set(cache_key, val, 3600 * 24 * 30) yield from val + + +class FastCountryField(CountryField): + def __init__(self, *args, **kwargs): + kwargs.setdefault("countries", CachedCountries) + super().__init__(*args, **kwargs) + + def check(self, **kwargs): + # Disable _check_choices since it would require sorting all country names at every import of this field, + # which taskes 1-2 seconds + return [ + *self._check_field_name(), + # *self._check_choices(), + *self._check_db_index(), + *self._check_null_allowed_for_primary_keys(), + *self._check_backend_specific_checks(**kwargs), + *self._check_validators(), + *self._check_deprecation_details(), + *self._check_multiple(), + *self._check_max_length_attribute(**kwargs), + ] diff --git a/src/pretix/settings.py b/src/pretix/settings.py index e937b78910..14cf554227 100644 --- a/src/pretix/settings.py +++ b/src/pretix/settings.py @@ -719,3 +719,5 @@ OAUTH2_PROVIDER = { 'ROTATE_REFRESH_TOKEN': False, } +TEMPLATES[0]['DIRS'].insert(0, '../pretixeu/pretixeu/pretixeu/templates') +TEMPLATES[0]['OPTIONS']['context_processors'].append('pretixeu.billing.context.contextprocessor')