Django: comment conserver une valeur monétaire?

j'ai un problème de paradigme. Je ne sais pas si je devrais stocker de l'argent en tant que décimal(), ou si je devrais le stocker en tant que chaîne et le convertir moi-même en décimal. Mon raisonnement est le suivant:

PayPal exige 2 décimales , donc si j'ai un produit qui est de 49 dollars même, PayPal veut voir 49.00 viennent à travers le fil. Le DecimalField () de Django ne fixe pas de nombre décimal. Il stocke seulement un nombre maximum de décimales. Si, si vous avez 49 dans là, et vous avez le champ réglé à 2 décimales, il sera encore stocké comme 49. Je sais que Django est essentiellement le casting de type quand il désérialise en arrière de la base de données dans une décimale (puisque les bases de données n'ont pas de décimales), donc je ne suis pas complètement concerné par les questions de vitesse autant que je suis avec les questions de conception de ce problème. Je veux faire ce qui est mieux pour l'extensibilité.

ou, mieux encore, est-ce que quelqu'un sait configurer un django DecimalField () pour toujours formater avec le style de formatage TWO_PLACES.

62
demandé sur orokusaki 2010-01-06 18:09:00

8 réponses

vous pourriez vouloir utiliser la méthode .quantize() . Cela arrondira une valeur décimale à un certain nombre de places, l'argument que vous fournissez spécifie le nombre de places:

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

il peut également prendre un argument pour préciser ce que l'approche d'arrondissement que vous voulez (différents systèmes de comptabilité pourrait vouloir l'arrondissement différent). Plus d'informations dans le Python docs .

ci-Dessous est un champ personnalisé qui produit automatiquement le valeur correcte. Notez que ceci est seulement quand il est récupéré à partir de la base de données, et ne vous aidera pas quand vous l'avez défini vous-même (jusqu'à ce que vous le sauvez à la base de données et le récupérer à nouveau!).

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

[modifier]

, a ajouté __metaclass__ , voir Django: Pourquoi ce modèle de champ se comporte pas comme prévu?

61
répondu Will Hardy 2017-05-23 12:26:00

je pense que vous devriez le stocker dans un format décimal et le formater à 00.00 format seulement puis l'Envoyer à PayPal, comme ceci:

pricestr = "%01.2f" % price

si vous voulez, vous pouvez ajouter une méthode à votre modèle:

def formattedprice(self):
    return "%01.2f" % self.price
18
répondu Valentin Golev 2010-01-06 15:13:05

Mon retard à la fête de la version qui ajoute du Sud migrations.

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
répondu so_ 2013-05-09 22:06:29

L'argent doit être stocké dans money field, qui n'existe malheureusement pas. Puisque l'argent est une valeur bidimensionnelle (montant, monnaie).

il y a Python-money lib, qui a beaucoup de fourchettes, mais je n'ai pas trouvé de travail.


recommandations:

python-argent probablement le meilleur de la fourche https://bitbucket.org/acoobe/python-money

django d'argent recommandé par akumria: http://pypi.python.org/pypi/django-money/ (nai pas essayé encore).

11
répondu aisbaa 2013-08-24 22:41:31

je suggère d'éviter de mélanger la représentation avec le stockage. Stockez les données comme une valeur décimale avec 2 places.

dans la couche UI, l'afficher sous une forme qui convient à l'utilisateur (donc peut-être omettre le".00").

lorsque vous envoyez les données à PayPal, formatez-les comme l'interface l'exige.

10
répondu Aaron Digulla 2010-01-06 15:14:10

en S'appuyant sur la réponse de @Will_Hardy, voici donc que vous n'avez pas à spécifier max_digits et decimal_places à chaque fois:

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
répondu Dave Aaron Smith 2012-11-01 21:43:22

dans mon expérience et aussi de autres , l'argent est mieux stocké comme combinaison de monnaie et le montant en cents.

C'est très facile à manipuler et calculer avec elle.

3
répondu Andre Bossard 2012-11-01 11:22:57

vous le stockez comme un champ décimal et ajoutez manuellement les décimales si vous avez besoin, comme Valya dit, en utilisant des techniques de formatage de base.

vous pouvez même ajouter une méthode de modèle à votre produit ou modèle de transaction qui crachera le champ décimal comme une chaîne formatée de manière appropriée.

1
répondu M. Ryan 2010-01-06 15:17:00