Django:我应该如何储存货币价值?

我遇到了一个范式问题。 我不知道是否应该将钱存储为Decimal(),或者如果我应该将其存储为string并将其自动转换为小数。 我的推理是这样的:

PayPal需要2位小数 ,所以如果我有一个产品甚至是49美元,PayPal希望看到49.00电线。 Django的DecimalField()不设置十进制数。 它只存储最大小数位数量。 所以,如果你有49个字段,并且字段设置为2个小数位,它仍然会将其存储为49.我知道,Django基本上是从数据库反序列化为Decimal时的types转换(因为数据库没有十进制字段),所以我并不是完全关心速度问题,就像我在处理这个问题的devise问题一样。 我想做可扩展性最好的。

或者,更好的是,有没有人知道如何configuration一个Django的DecimalField()来始终使用TWO_PLACES格式样式进行格式化。

您可能想要使用.quantize()方法。 这会将十进制值舍入到一定数量的位置,您提供的参数指定了位置的数量:

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

它也可以用一个参数来指定你想要的舍入方法(不同的会计系统可能需要不同的舍入)。 Python文档中的更多信息。

以下是自动生成正确值的自定义字段。 请注意,这只是从数据库中检索时,并不会帮助你,当你自己设置它(直到你把它保存到数据库并再次检索!)。

 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 

[编辑]

添加__metaclass__ ,请参阅Django:为什么此自定义模型字段没有按预期行为?

我认为你应该以十进制格式存储它,并将其格式化为00.00格式,然后将其发送到PayPal,如下所示:

 pricestr = "%01.2f" % price 

如果你愿意,你可以在你的模型中添加一个方法:

 def formattedprice(self): return "%01.2f" % self.price 

我迟到了党的版本,增加了南迁移。

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

我build议避免混合表示与存储。 将数据存储为2位的十进制值。

在UI层中,以适合用户的forms显示它(所以也许省略“.00”)。

当您将数据发送到PayPal时,请按照界面要求进行格式化。

钱应该存储在货币领域,这可悲的是不存在。 由于货币是二维价值(金额,货币)。

python钱 ,有很多叉子,但我还没有find工作。


build议:

python钱可能是最好的叉https://bitbucket.org/acoobe/python-money

由akumria推荐的django-money : http ://pypi.python.org/pypi/django-money/(还没有试过那个)。

基于@ Will_Hardy的回答,这里是每次都不需要指定max_digits和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 

根据我的经验和其他人的经验,货币最好以货币和金额的组合存储。

这很容易处理和计算。

如Valya所说,如果需要,可以使用基本的格式化技术将其存储为DecimalField并手动添加小数。

您甚至可以向您的产品或事务模型中添加一个模型方法,将DecimalField作为适当格式的string进行吐出。