Django, DecimalField mit Komma

Django, Flask, Bottle, WSGI, CGI…
Antworten
geraldfo
User
Beiträge: 44
Registriert: Samstag 28. Januar 2023, 20:19
Wohnort: Nähe Wien

Hallo allerseits,

ich arbeite an einer Django-Anwendung, die nur von deutschsprachigen Usern genutzt werden soll.

Felder für Geldbeträge (DecimalField) sollen in der Benutzeroberfläche immer ein Komma als Dezimaltrennzeichen haben.
Dieses Verhalten soll unabhängig von Einstellungen am Client-System sein.

Der Output von Zahlen funktioniert.

Problem:
Der Form-Input funktioniert nicht.
Wenn ich eine Dezimalzahl eingebe, komme ich aus der Edit-Maske nicht hinaus.
Eingaben mit Punkt als Dezimaltrennzeichen werden angenommen.

django.middleware.locale.LocaleMiddleware
Wenn ich das richtig verstehe, dient das dem Wechseln zwischen verschiedenen Lokalisierung und ist in meinem Fall unnötig.

Settings

Code: Alles auswählen

LANGUAGE_CODE = 'de-de'
USE_I18N = True
USE_L10N = True
DECIMAL_SEPARATOR = ','
Model

Code: Alles auswählen

class Product(models.Model):
    designation = models.CharField(max_length=100, blank=False,
        verbose_name='Bezeichnung')
    price = models.DecimalField(max_digits=8, decimal_places=2, blank=False,
        verbose_name='Preis')

    def get_absolute_url(self):
        return reverse('product_detail', kwargs={'pk': self.pk})
Form

Code: Alles auswählen

class ProductForm(forms.ModelForm):
    class Meta:
        model = Product
        fields = '__all__'
        localized_fields = '__all__'
(Hab’s auch mit einem Form auf Basis von forms.Form probiert.)

View

Code: Alles auswählen

class ProductUpdateView(UpdateView):
    model = Product
    template_name = 'product_edit.html'
    fields = '__all__'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        print(self.object)
        context['form'] = ProductForm(instance=self.object)
        return context
Edit-Template

Code: Alles auswählen

{% extends "base.html" %}

{% block content %}

{% load i18n %}
{% load l10n %}

<h1>Edit product</h1>
<div>ID: {{product.id}}</div>

{% localize on %}

{{ form.non_field_errors }}

<form action="" method="post">{% csrf_token %}
    {{ form.as_div }}
    <input type="submit" value="Speichern">
</form>

{% endlocalize %}

{% endblock content %}
Hat jemand von euch etwas Ähnliches am laufen?
Wäre für einen hilfreichen Tipp sehr dankbar.

Grüße Gerald
Pitwheazle
User
Beiträge: 873
Registriert: Sonntag 19. September 2021, 09:40

Ich habe was ähnliches laufen und hatte auch schon ein ähnliches Problem (viewtopic.php?t=54676). Ich habe aber immer noch keinen rechten Durchblick bei Django (etwa 90% der Fragen hier sind von mir). Bei mir funktioniert es. Wenn ich mich recht erinnere, war das Problem nach dem Hinweis von @sparrow mit "USE_L10N" gelöst - aber das hast du ja auch eingefügt. Ich hatte auch das Problem mit den Kommas und Punkten und wenn ich das richtig verstanden habe, kann man es nicht komplett lösen indem man einfach auf de-de stellt. Ich habe ein Projekt zum Rechnen-üben. Je nachdem, ob ich nur eine Zahl als Eingabe erwarte (dann steht in protokoll.value eine Zahl) oder einen String, schalte ich meine zwei Views um:

Code: Alles auswählen

                if protokoll.value:
                    form = AufgabeFormZahl(req.POST)
                else:
                    form = AufgabeFormStr(req.POST)
meine views:

Code: Alles auswählen

class AufgabeFormZahl(forms.Form):
    eingabe = forms.DecimalField(label='', max_digits=15,
                                decimal_places=5, widget=forms.NumberInput(attrs={'autofocus': True, 'autocomplete': 'off'}))
    
class AufgabeFormStr(forms.Form):
    eingabe = forms.CharField(label='', localize=True, widget=forms.TextInput(attrs={'autofocus': True, 'autocomplete': 'off'}))
in settings.py steht nur

Code: Alles auswählen

LANGUAGE_CODE = 'de-de'
USE_L10N = True
USE_I18N = True
(kein "DECIMAL_SEPARATOR = ','")
Die Form " eingabe = forms.DecimalField" akkzeptiert bei mir Komma und Punkt. Die Eingabe überprüfe ich mit "eingabe == value" (das sind intern Zahlen mit Punkt, egal ob mit Punkt oder Komma eingegeben). Zur Ausgabe der Zahlen (mit Komma) benutze ich Strings und "replace(".", ",")".

Ich habe keine Idee, wo ich noch eine andere Einstellung gemacht hätte.
geraldfo
User
Beiträge: 44
Registriert: Samstag 28. Januar 2023, 20:19
Wohnort: Nähe Wien

@Pitwheazle:
Herzlichen Dank für die Antwort!
Wenn ich das richtig verstehe, änderst du selbst das Dezimaltrennzeichen der eingegebenen Zahlen von Komma auf Punkt. Müsste ich dann auch machen, wenn es keine schönere Lösung gibt.

Zwei Sachen habe ich noch ausprobiert:

1)
FloatField
Verhält sich betreffend Komma wie das DecimalField.

2)
DECIMAL_SEPARATOR = ','
Ob in den Settings das drinsteht oder nicht scheint egal zu sein.
Benutzeravatar
sparrow
User
Beiträge: 4193
Registriert: Freitag 17. April 2009, 10:28

@geraldfo: Das sollte sich alles über die Lokalisierung lösen lassen.
geraldfo
User
Beiträge: 44
Registriert: Samstag 28. Januar 2023, 20:19
Wohnort: Nähe Wien

Das versuche ich ja. Aber mir ist nicht klar was ich tun muss, damit es funktioniert.
Benutzeravatar
sparrow
User
Beiträge: 4193
Registriert: Freitag 17. April 2009, 10:28

@geralfo: Ich kann dein Problem ehrlich gesagt nicht nachvollziehen. Frisch angelegtes Django Projekt.

settings.py:

Code: Alles auswählen

LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_TZ = True
views.py:

Code: Alles auswählen

from django.template.response import TemplateResponse

from .forms import ExampleForm

def testview(request):
    if request.method == 'POST':
        form = ExampleForm(request.POST)
        if form.is_valid():
            print("Is valid!")
            print("cleaned data:", form.cleaned_data)
        else:
            print("NOT valid")
    else:
        form = ExampleForm()
    return TemplateResponse(request, "test.html", {'form': form})
forms.py

Code: Alles auswählen

from django import forms

class ExampleForm(forms.Form):
    integer = forms.IntegerField(label="An Integer")
    float = forms.FloatField(label="A Float")
    decimal = forms.DecimalField(label="A Decimal", max_digits=5, decimal_places=2)
test.html

Code: Alles auswählen

<html>
    <body>
        <form action="" method="post">
            {% csrf_token %}
            {{ form }}
            <input type="submit" value="Submit">
        </form>
    </body>
</html>

Wenn der Browser als Lokalisierung de-DE mitschickt kann man "," als Dezimalkennzeichen verwenden und bekommt die standardmäßig auch in den Feldern angezeigt, während der Wert als entsprechende Fließkommazahl und Decimal mit "." im Backend ankommt.

Edit: Wobei ich glaube, dass das gar nicht von der gesendeten Sprache abhänt, sondern von der "locale" des Systems.
Und mir fallen relativ wenig Gründe ein, warum man das Dezimalkennzeichen von der Locale trennen sollte.
Benutzeravatar
sparrow
User
Beiträge: 4193
Registriert: Freitag 17. April 2009, 10:28

Ah sorry, ich sehe jetzt erst, dass du das Komma ja _immer_ als Trennzeichen haben willst.

Ich habe es ja im vorherigen Post schon angedeutet: Das würde ich nicht machen. Dann verhält sich deine Webseite nämlich anders als alle anderen Webseiten, die der Benutzer aufruft. Auch die Benutzung von Djangos Forms macht dann nur noch wenig Spaß, weil ja schon auf Browserseite locale ausgewertet werden.
geraldfo
User
Beiträge: 44
Registriert: Samstag 28. Januar 2023, 20:19
Wohnort: Nähe Wien

@sparrow: Vielen Dank für deine Beiträge!
Felder für Geldbeträge (DecimalField) sollen in der Benutzeroberfläche immer ein Komma als Dezimaltrennzeichen haben.
Dieses Verhalten soll unabhängig von Einstellungen am Client-System sein.
Ja, das war meine ursprüngliche Idee. Aber vielleicht war die Idee doch nicht so gut.
Vielleicht ist es doch besser, wenn das Dezimaltrennzeichen von der Spracheinstellung am Client abhängt.

Melde mich demnächst. Wahrscheinlich morgen (Sonntag).

Grüße Gerald
geraldfo
User
Beiträge: 44
Registriert: Samstag 28. Januar 2023, 20:19
Wohnort: Nähe Wien

@sparrow: Nochmals danke! Ich konnte dein Beispiel nachvollziehen.

Getestet hab ich das bis jetzt nur mit dem Google Chrome-Browser unter Linux.

Die Lokalisierung des Formulars spielt sich offenbar nur im UI des Browsers ab.

Wie die Zahlen im Formular stehen hängt tatsächlich von der Locale des Client-Systems ab.
In Chrome kann man die UI-Sprache abändern. Bei der Formular-Darstellung ist das aber irrelevant.

Im HTML-Quelltext haben die Zahlenwerte in jedem Fall Punkte als Dezimaltrennzeichen.

Das input-Element muss vom Typ number sein.

Bei Nutzung von ModelForm werden input-Elemente vom Typ text generiert.
Da weiß ich nicht, wie man das ändern könnte.

Falle, wenn man mit deutscher Oberfläche arbeitet:
Alternativ zum Komma wird auch der Punkt als Dezimaltrennzeichen angenommen.
Versucht jemand den Wert Tausend in der Form '1.000' einzugeben, passiert Folgendes:
Bei Integer- und Float-Feld: aus der Tausend wird eine Eins!!!
Bei Decimal-Feld kommt im konkreten Fall die englische Meldung 'Ensure that there are no more than 2 decimal places.'

Schönen Sonntag allerseits!
Antworten