Используйте различные десятичные разделители для валидации на фронтенде в интерфейсе Django Admin

Я просмотрел все похожие вопросы на stackoverflow и перепробовал почти все. Кажется, это легко сделать: Я просто хочу разрешить , в качестве десятичного разделителя для FloatField в интерфейсе администратора Django. На данный момент это зависит от локализации, но я всегда хочу разрешить это. Для меня было бы даже нормально, если бы это было просто TextInput, но для работы мне нужно ,. Установка DECIMAL_SEPARATOR в settings.py не работает.

Мой вопрос похож на этот 6-летний вопрос без ответа: Как переопределить только DECIMAL_SEPARATOR в django, сохранив при этом локализацию?

Мне удалось использовать виджет TextInput для FloatFields следующим образом:

class ExampleAdminForm(forms.ModelForm):
    class Meta:
        model = Example

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for key, value in self.fields.items():
            if isinstance(value, FloatField):
                self.fields[key].widget = TextInput()  

Виджет работает, но ввод типа 1,23 приводит к сообщению об ошибке Enter a number. Я не могу найти, где происходит валидация, поскольку validate() из FloatField никогда не срабатывает. Есть ли способ, например, переопределить FloatField или виджет TextInput, чтобы разрешить использование других десятичных разделителей? Или какой-либо другой способ?

Исправьте метод to_python или используйте пользовательский класс поля формы, который переопределяет этот метод.

Ответ на один вопрос:

self.fields[key].to_python = lambda v: self.fields[key].__class__.to_python(self.fields[key], '.'.join(v.rsplit(',', 1)) if len(v.rsplit(',', 1)[-1]) < 3 else v)

В качестве функции-обертки:

self.fields[key].to_python = allow_comma_decimal_separator(self.fields[key].to_python)
def allow_comma_decimal_separator(old_to_python):
    def to_python(value):
        if ',' in value:
            lvalue, decimals = value.rsplit(',', 1)
            if len(decimals) < 3:
                value = '.'.join((lvalue, decimals))
        return old_to_python(value)
    return to_python

т.е. если в значении стоит запятая, а подстрока после самой правой запятой имеет длину меньше 3 (поэтому мы предполагаем, что это не разделитель тысяч), то мы заменяем запятую точкой, соединяя подстроку до и после точкой.

Для явных полей, использующих многоразовый класс поля формы

Это будет считаться менее "халтурным".

class AllowCommaDecimalSeparatorFloatField(forms.FloatField):

    def to_python(self, value):
        if ',' in value:
            lvalue, decimals = value.rsplit(',', 1)
            if len(decimals) < 3:
                value = '.'.join((lvalue, decimals))
        return super().to_python(value)

В классе Meta вашей формы:

field_classes = {
    'myfield': AllowCommaDecimalSeparatorFloatField,
}

Для всех FloatFields

Чтобы повлиять на все экземпляры FloatField (еще не инстанцированные), поместите это в начало вашего модуля:

old_to_python = forms.FloatField.to_python


def to_python(self, value):
    if ',' in value:
        lvalue, decimals = value.rsplit(',', 1)
        if len(decimals) < 3:
            value = '.'.join((lvalue, decimals))
    return old_to_python(self, value)


forms.FloatField.to_python = to_python
Вернуться на верх