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.
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?
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
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'])
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).
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.
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
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.
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.