2010-01-06 91 views
61

我在这里遇到了范例问题。我不知道是否应该将钱存储为Decimal(),或者如果我应该将其存储为字符串并将其自动转换为小数。我的推理是这样的:Django:我应该如何存储货币价值?

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

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

+1

re“因为数据库没有小数字段”; Microsoft Sql Server同时具有“十进制”和“金钱”数据类型http://msdn.microsoft.com/en-us/library/aa258271%28v=sql.80%29.aspx – 2011-05-16 20:47:33

回答

60

您可能想要使用.quantize()方法。这将圆一个十进制值达到一定数量的地方,你所提供的参数指定的名额:

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

它还可以接受一个参数指定你想要什么四舍五入的方法(不同的会计系统可能需要不同的四舍五入)。更多信息在Python docs

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

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: Why does this custom model field not behave as expected?

+0

优秀。谢谢你。这是你需要做的全部功能自定义模型领域? (即,除了格式化之外,它的工作方式与DecimalField完全相同)。 – orokusaki 2010-01-08 14:15:47

+1

另外,存储自定义字段的约定是什么?我想我会把它放在根项目文件夹中。 – orokusaki 2010-01-08 14:16:20

+0

谢谢,我只是用这个。我注意到每次使用CurrencyField时必须重复max_digits和decimal_places属性,所以我发布了一个建立在您的解决方案上的答案。 – 2012-11-01 21:44:58

18

我觉得你应该把它保存在一个十进制格式,并格式化,以00.00格式只有后发送给贝宝,像这样:

pricestr = "%01.2f" % price 

如果你愿意,你可以将方法添加到您的型号:

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

另外:将货币存储在数据库中以及金额。钱不只是一个数字。 – 2010-01-06 15:22:04

+0

的确如此。感谢Shiny先生和Shiny先生 – orokusaki 2010-01-06 15:24:43

+6

先生,如果您知道所有货币都相同,您肯定不需要将货币存储在数据库中。事实上,大多数应用程序不处理多种货币。 – 2010-01-06 17:01:13

10

我建议避免混合表示与存储。将数据存储为2位的小数值。

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

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

1

您将它作为DecimalField存储,并根据Valya的说法,使用基本的格式化技术手动添加小数点。

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

11

资金应存放在货币领域,遗憾的是不存在的。由于货币是二维价值(金额,货币)。

还有python-money lib,有很多叉子,但我还没有找到工作之一。


建议:

蟒蛇货币可能是最好的叉通过akumria推荐https://bitbucket.org/acoobe/python-money

Django的钱http://pypi.python.org/pypi/django-money/(还没有尝试过一个尚未)。

+0

推荐https://github.com/reinbach/django-money而不是akumria,因为它有一些错误修正并通过pip安装。 – 2013-05-10 23:57:07

3

根据我的经验和others,货币最好存储为货币和金额的组合。

它很容易处理和计算。对@ Will_Hardy的回答

+0

请注意,1个未成年人(如cent)等于0.01主要(美元)的货币并非如此。 – ElmoVanKielmo 2015-05-14 07:22:49

+0

我不应该使用术语“美分”,但更多的是最小的货币没有进一步可分割的单位。 – 2015-05-22 12:41:49

+0

尽管如此,您的应用程序仍然不支持主要和次要不同比率的货币。 – ElmoVanKielmo 2015-05-22 13:05:43

4

大厦,这里是这样你就不必指定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 
+0

我有一个类似的字段,但我使用'defaults = {...'和'defaults.update(** kwargs)'模式。 – orokusaki 2012-11-02 18:00:56

13

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

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

“['^ application \ .fields \ .CurrencyField']”中的路径是否必须根据我的项目进行更改?谢谢 – pymarco 2013-12-05 22:40:35