diff --git a/src/pretix/api/serializers/fields.py b/src/pretix/api/serializers/fields.py new file mode 100644 index 0000000000..3bd949d681 --- /dev/null +++ b/src/pretix/api/serializers/fields.py @@ -0,0 +1,29 @@ +from collections import OrderedDict + +from rest_framework import serializers + + +def remove_duplicates_from_list(data): + return list(OrderedDict.fromkeys(data)) + + +class ListMultipleChoiceField(serializers.MultipleChoiceField): + def to_internal_value(self, data): + if isinstance(data, str) or not hasattr(data, '__iter__'): + self.fail('not_a_list', input_type=type(data).__name__) + if not self.allow_empty and len(data) == 0: + self.fail('empty') + + internal_value_data = [ + super(serializers.MultipleChoiceField, self).to_internal_value(item) + for item in data + ] + + return remove_duplicates_from_list(internal_value_data) + + def to_representation(self, value): + representation_data = [ + self.choice_strings_to_values.get(str(item), item) for item in value + ] + + return remove_duplicates_from_list(representation_data) diff --git a/src/pretix/base/settings.py b/src/pretix/base/settings.py index f7e12a2528..8e211729b4 100644 --- a/src/pretix/base/settings.py +++ b/src/pretix/base/settings.py @@ -19,6 +19,7 @@ from i18nfield.forms import I18nFormField, I18nTextarea, I18nTextInput from i18nfield.strings import LazyI18nString from rest_framework import serializers +from pretix.api.serializers.fields import ListMultipleChoiceField from pretix.api.serializers.i18n import I18nField from pretix.base.models.tax import TaxRule from pretix.base.reldate import ( @@ -639,7 +640,7 @@ DEFAULTS = { 'locales': { 'default': json.dumps([settings.LANGUAGE_CODE]), 'type': list, - 'serializer_class': serializers.MultipleChoiceField, + 'serializer_class': ListMultipleChoiceField, 'serializer_kwargs': dict( choices=settings.LANGUAGES, required=True, diff --git a/src/tests/api/test_events.py b/src/tests/api/test_events.py index c3f89287ef..2b135bec43 100644 --- a/src/tests/api/test_events.py +++ b/src/tests/api/test_events.py @@ -1032,6 +1032,32 @@ def test_patch_event_settings(token_client, organizer, event): ) assert resp.status_code == 405 + locales = event.settings.locales + + resp = token_client.patch( + '/api/v1/organizers/{}/events/{}/settings/'.format(organizer.slug, event.slug), + { + 'locales': event.settings.locales + ['de', 'de-informal'], + }, + format='json' + ) + assert resp.status_code == 200 + assert set(resp.data['locales']) == set(locales + ['de', 'de-informal']) + event.settings.flush() + assert set(event.settings.locales) == set(locales + ['de', 'de-informal']) + + resp = token_client.patch( + '/api/v1/organizers/{}/events/{}/settings/'.format(organizer.slug, event.slug), + { + 'locales': locales, + }, + format='json' + ) + assert resp.status_code == 200 + assert set(resp.data['locales']) == set(locales) + event.settings.flush() + assert set(event.settings.locales) == set(locales) + @pytest.mark.django_db def test_patch_event_settings_validation(token_client, organizer, event):