# -*- coding: utf-8 -*-
# This file is part of Shuup.
#
# Copyright (c) 2012-2021, Shuup Commerce Inc. All rights reserved.
#
# This source code is licensed under the OSL-3.0 license found in the
# LICENSE file in the root directory of this source tree.
from django import forms
from django.core.exceptions import ValidationError
from django.utils.translation import ngettext
from shuup.utils.django_compat import force_text
[docs]class RelaxedModelChoiceField(forms.ModelChoiceField):
# `RelaxedModelChoiceField`s allow manually setting `choices` with full validation
# as an improvement over the normal `ModelChoiceField`.
[docs] def to_python(self, value):
try:
return super(RelaxedModelChoiceField, self).to_python(value)
except ValidationError as verr:
if verr.code == "invalid_choice":
# If the original code declared this as invalid, see if we have custom choices.
if hasattr(self, "_choices"):
# Stringly [sic] typed comparison...
value = force_text(value)
key = self.to_field_name or "pk"
for (pk, obj) in self._choices:
if force_text(pk) == value or force_text(getattr(obj, key, "")) == value:
if obj is None or isinstance(obj, self.queryset.model):
return obj
raise verr # Just reraise the original exception then, but from here for clarity
[docs]class TypedMultipleChoiceWithLimitField(forms.TypedMultipleChoiceField):
def __init__(self, min_limit=None, max_limit=None, **kwargs):
self.min_limit = min_limit
self.max_limit = max_limit
super().__init__(**kwargs)
[docs] def clean(self, value):
value = super().clean(value)
if self.min_limit is not None and len(value) < self.min_limit:
error_message = ngettext(
"You can't select less than {min_limit} item.",
"You can't select less than {min_limit} items.",
self.min_limit,
)
raise forms.ValidationError(error_message.format(min_limit=self.min_limit))
if self.max_limit is not None and len(value) > self.max_limit:
error_message = ngettext(
"You can't select more than {max_limit} item.",
"You can't select more than {max_limit} items.",
self.max_limit,
)
raise forms.ValidationError(error_message.format(max_limit=self.max_limit))
return value