Django: jak przechowywać wartość pieniężną?

Mam problem z paradygmatem. Nie wiem, czy powinienem przechowywać pieniądze jako dziesiętne (), czy też powinienem przechowywać je jako ciąg znaków i przekonwertować na dziesiętne. Moje rozumowanie jest takie:

PayPal wymaga 2 miejsc po przecinku, więc jeśli mam produkt, który jest 49 dolarów nawet, PayPal chce zobaczyć 49.00 natknąć się na drut. Funkcja Django DecimalField() nie ustawia liczby dziesiętnej. Przechowuje tylko maksymalną liczbę miejsc po przecinku. Więc jeśli masz tam 49 i masz pole ustawione na 2 miejsca po przecinku, nadal będzie przechowywać je jako 49. Wiem, że Django jest w zasadzie odlewaniem typu, gdy deserializuje się z powrotem z bazy danych do dziesiętnego (ponieważ bazy danych nie mają pól dziesiętnych), więc nie jestem całkowicie zainteresowany kwestiami prędkości, tak jak jestem z problemami projektowymi tego problemu. Chcę zrobić to, co najlepsze dla rozciągliwości.

Albo, jeszcze lepiej, czy ktoś wie jak skonfigurować Django DecimalField() tak, aby zawsze formatował z formatowaniem TWO_PLACES styl.

Author: orokusaki, 2010-01-06

8 answers

Możesz użyć metody .quantize(). Spowoduje to zaokrąglenie wartości dziesiętnej do określonej liczby miejsc, podany argument określa liczbę miejsc:

>>> from decimal import Decimal
>>> Decimal("12.234").quantize(Decimal("0.00"))
Decimal("12.23")

Można również użyć argumentu, aby określić, jakie podejście do zaokrąglania chcesz (różne systemy księgowe mogą chcieć innego zaokrąglenia). Więcej informacji w Python docs .

Poniżej znajduje się pole niestandardowe, które automatycznie tworzy prawidłową wartość. Zauważ, że jest to tylko wtedy, gdy jest pobierany z bazy danych, i nie pomoże Ci, gdy ustawisz go samodzielnie (dopóki nie zapiszesz go do db i ponownie go odzyskasz!).

from django.db import models
from decimal import Decimal
class CurrencyField(models.DecimalField):
    __metaclass__ = models.SubfieldBase

    def to_python(self, value):
        try:
           return super(CurrencyField, self).to_python(value).quantize(Decimal("0.01"))
        except AttributeError:
           return None

[edytuj]

Dodano __metaclass__, Zobacz Django: dlaczego to pole modelu niestandardowego nie zachowuje się zgodnie z oczekiwaniami?

 61
Author: Will Hardy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:26:00

Myślę, że powinieneś zapisać go w formacie dziesiętnym i sformatować do formatu 00.00 Tylko , a następnie wysłać go do PayPal, TAK:

pricestr = "%01.2f" % price

Jeśli chcesz, możesz dodać metodę do swojego modelu:

def formattedprice(self):
    return "%01.2f" % self.price
 18
Author: Valentin Golev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-06 15:13:05

Mój spóźniony do wersji partyjnej, która dodaje Południowe migracje.

from decimal import Decimal
from django.db import models

try:
    from south.modelsinspector import add_introspection_rules
except ImportError:
    SOUTH = False
else:
    SOUTH = True

class CurrencyField(models.DecimalField):
    __metaclass__ = models.SubfieldBase

    def __init__(self, verbose_name=None, name=None, **kwargs):
        decimal_places = kwargs.pop('decimal_places', 2)
        max_digits = kwargs.pop('max_digits', 10)

        super(CurrencyField, self). __init__(
            verbose_name=verbose_name, name=name, max_digits=max_digits,
            decimal_places=decimal_places, **kwargs)

    def to_python(self, value):
        try:
            return super(CurrencyField, self).to_python(value).quantize(Decimal("0.01"))
        except AttributeError:
            return None

if SOUTH:
    add_introspection_rules([
        (
            [CurrencyField],
            [],
            {
                "decimal_places": ["decimal_places", { "default": "2" }],
                "max_digits": ["max_digits", { "default": "10" }],
            },
        ),
    ], ['^application\.fields\.CurrencyField'])
 13
Author: so_,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-09 22:06:29

Pieniądze powinny być przechowywane w polu pieniędzy, które Niestety nie istnieje. Ponieważ pieniądz jest dwuwymiarową wartością(kwota, waluta).

Jest python-money lib, który ma wiele widelców, ale nie znalazłem żadnego działającego.


Rekomendacje:

Python-money prawdopodobnie najlepszy widelec https://bitbucket.org/acoobe/python-money

Django-money polecane przez akumria: http://pypi.python.org/pypi/django-money / (havent próbowałem tego jeszcze).

 11
Author: aisbaa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-24 22:41:31

Proponuję unikać mieszania reprezentacji z Magazynem. Zapisz dane jako wartość dziesiętną z 2 miejscami.

W warstwie UI, wyświetl go w formie, która jest odpowiednia dla użytkownika (więc może pominąć ".00").

Gdy wysyłasz dane do PayPal, sformatuj je zgodnie z wymaganiami interfejsu.

 10
Author: Aaron Digulla,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-06 15:14:10

Bazując na odpowiedzi @ Will_Hardy, tutaj jest tak, abyś nie musiał za każdym razem podawać max_digits i decimal_places:

from django.db import models
from decimal import Decimal


class CurrencyField(models.DecimalField):
  __metaclass__ = models.SubfieldBase

  def __init__(self, verbose_name=None, name=None, **kwargs):
    super(CurrencyField, self). __init__(
        verbose_name=verbose_name, name=name, max_digits=10,
        decimal_places=2, **kwargs)

  def to_python(self, value):
    try:
      return super(CurrencyField, self).to_python(value).quantize(Decimal("0.01"))
    except AttributeError:
      return None
 4
Author: Dave Aaron Smith,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-01 21:43:22

Z mojego doświadczenia, a także z innych, pieniądze najlepiej przechowywać jako połączenie waluty i kwoty w centach.

Jest bardzo łatwy w obsłudze i obliczyć z nim.

 3
Author: Andre Bossard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-01 11:22:57

Zapisujesz go jako miejsce dziesiętne i ręcznie dodajesz miejsca dziesiętne, jeśli potrzebujesz, jak powiedziała Valya, przy użyciu podstawowych technik formatowania.

Możesz nawet dodać metodę modelu do produktu lub modelu transakcji, która wypluje pole dziesiętne jako odpowiednio sformatowany ciąg znaków.

 1
Author: M. Ryan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-06 15:17:00