Reputation: 983
I looked through every single similar question on stackoverflow and tried nearly everything.
It seems easy to do: I just want to allow ,
as decimal separator for a FloatField
in the Django admin interface. At the moment, it depends on the localization, but I always want to allow it. It would even be ok for me, if it's just a TextInput
, but I need ,
to work. Setting DECIMAL_SEPARATOR
in settings.py does not work.
My question is similar to this 6 year old, unanswered one: How to only override DECIMAL_SEPARATOR in django while keeping localization intact?
I managed to use the TextInput
widget for FloatFields like this:
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()
The widget works, but an input like 1,23
leads to an error message Enter a number
. I'd have to implement my own, custom FloatField that overrides the sanitizing in to_python
to allow for differenct decimal separators, but then I'd still need a different widget. This seems awfully complicated, is there a better way?
If not, how can I change the frontend validation in the widget to ignore localization and use my own regex pattern?
Upvotes: 1
Views: 338
Reputation: 43083
Patch the to_python
method, or use a custom form field class that overrides that method.
One-liner answer:
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)
As a wrapper function:
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
i.e. If a comma is in the value, and the substring after the rightmost comma has a length of less than 3 (so we assume it's not a thousand separator), then we replace that comma with a period by joining the substring before and after with a period.
This would be considered less "hacky".
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)
In your form's Meta
class:
field_classes = {
'myfield': AllowCommaDecimalSeparatorFloatField,
}
To affect all FloatField
instances (not instantiated yet), place this at the top of your module:
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
Upvotes: 1