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

oro*_*aki 63 python django currency decimal django-models

我在这里遇到了范式问题.我不知道是否应该将钱存储为Decimal(),或者我是否应该将其存储为字符串并将其自身转换为小数.我的理由是这样的:

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

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

Wil*_*rdy 62

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

>>> from decimal import Decimal
>>> Decimal("12.234").quantize(Decimal("0.00"))
Decimal("12.23")
Run Code Online (Sandbox Code Playgroud)

它还可以通过参数来指定您想要的舍入方法(不同的会计系统可能需要不同的舍入).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
Run Code Online (Sandbox Code Playgroud)

[编辑]

添加__metaclass__,请参阅Django:为什么此自定义模型字段的行为不符合预期?


Val*_*lev 18

我想你应该将其存储在十进制格式,并格式化,以00.00格式后发送给贝宝,就像这样:

pricestr = "%01.2f" % price
Run Code Online (Sandbox Code Playgroud)

如果需要,可以向模型添加方法:

def formattedprice(self):
    return "%01.2f" % self.price
Run Code Online (Sandbox Code Playgroud)

  • 另外:将货币存储在数据库中以及金额.金钱不仅仅是一个数字. (6认同)
  • Shiny先生,如果您知道所有货币都是相同的,您当然不需要将货币存储在数据库中.事实上,大多数应用程序不处理多种货币. (6认同)

so_*_*so_ 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'])
Run Code Online (Sandbox Code Playgroud)


ais*_*baa 12

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

python-money lib,有很多分叉,但我找不到工作.


建议:

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

akumria推荐的django-money:http://pypi.python.org/pypi/django-money/ (尚未尝试过那个).


Aar*_*lla 10

我建议避免将表示与存储混合.将数据存储为具有2个位置的十进制值.

在UI层中,以适合用户的形式显示它(因此可能省略".00").

将数据发送到PayPal时,请按接口要求对其进行格式化.