Django - Serializer und Decimal-Fields

Django, Flask, Bottle, WSGI, CGI…
Antworten
robin_
User
Beiträge: 48
Registriert: Montag 3. August 2020, 17:59

Hi,

mir ist folgendes Aufgefallen, dazu mal hier ein Ausschnitt meines Models:

Code: Alles auswählen

unit_price = models.DecimalField(default=Decimal('10.00'), null=True, decimal_places=2, max_digits=12)
    quantity = models.DecimalField(default=Decimal('1.00'), null=True, max_digits=5, decimal_places=2)
    tax_rate = models.DecimalField(default=Decimal('0.19'), null=True, decimal_places=2, max_digits=3)
Da es um Währungsbeträge geht, möchte ich Decimal benutzen, um die Genauigkeit zu wahren.

Dazu habe ich noch ein paar Properties:

Code: Alles auswählen

@property
    def item_net_amount(self):
        return self.quantity *  self.unit_price

    @property
    def item_gross_amount(self):
        return self.item_net_amount+ self.item_tax_amount 

    @property
    def item_tax_amount(self):
        return self.item_net_amount * self.tax_rate
Wenn ich nun, ganz normal, serialisiere:

Code: Alles auswählen

class InvoiceSerializer(serializers.ModelSerializer):
    class Meta:
        model = Invoice
        fields = "__all__"
spuckt mir Postman folgendes aus:

Code: Alles auswählen

 {
        "id": 3,
        "unit_price": "10.00",
        "quantity": "1.00",
        "tax_rate": "0.19",
        "item_net_amount": 10.0,
        "item_gross_amount": 11.9,
        "item_tax_amount": 1.9
    },
Also, die felder unit_price, quantity und tax_rate werden als String serialisiert, die Werte der @property als Decimalzahlen...

Nun meine Fragen:

1) Warum gibt es überhaupt einen unterschied?
2) Welche Variante sollte beim serialisieren bevorzugt werden? Ich denke, dass die String-Variante nicht verkehrt ist, um mögliche ungewollte Umwandlungen in float zu verhindern?

Vielen Dank, schönes Wochenende euch!

LG Robin
Benutzeravatar
sparrow
User
Beiträge: 4165
Registriert: Freitag 17. April 2009, 10:28

@robin_: Ich würde sagen, das hat nichts damit zu tun, dass es die @property-Funktionen trifft. Schau dir mal an, von welchem Typ die verwendeten Werte in den Funktionen sind. Wenn du zwei Werte vom Typ "float" miteinander addierst, kommt dabei nicht magisch eine Decimal-Instanz heraus.
Benutzeravatar
__blackjack__
User
Beiträge: 13006
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@sparrow: Da werden aber nirgends Werte vom Typ `float` miteinander addiert. Das sollten ja alles `Decimal`-Objekte sein.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Benutzeravatar
sparrow
User
Beiträge: 4165
Registriert: Freitag 17. April 2009, 10:28

@__blackjack__: Oh, du hast recht. item_net_amount hat es nicht bis in meine Wahrnehmung geschafft. War wohl noch früh. ;)

@robin_: Das ist der Serializer aus dem restframework, oder?
Bei der Serialisierung wird - wenn vorhanden - der Typ des Feldes im Model ausgewertet. In serializer.data liegt bereits vor dem Parsen zu json eine Zeichnekette, wenn es sich um ein DecimalField handelt - bei den Properties kommt ein Decimal-Object bei dem JSONRenderer an - und der macht daraus ein float.
Du kannst dem Serializer sagen, dass es sich bei den Feldern um Decimal-Felder handelt, musst dann aber - wie dort auch - decimal_places und max_digits angeben:

Code: Alles auswählen

class InvoiceSerializer(serializers.ModelSerializer):

    item_net_amount = serializers.DecimalField(max_digits=5, decimal_places=2)
    # [...]
    
    class Meta:
        model = Invoice
        fields = "__all__
robin_
User
Beiträge: 48
Registriert: Montag 3. August 2020, 17:59

So, bin bisher nicht dazu gekommen, dass anzupassen.

Vielen Dank euch beiden für die Antwort, der Vorschlag funktioniert!
Antworten