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

Python Django:我应该如何存储货币价值?,python,django,django-models,decimal,currency,Python,Django,Django Models,Decimal,Currency,我在这里遇到了一个范例问题。我不知道是应该将钱存储为Decimal(),还是应该将其存储为字符串并自己将其转换为十进制。我的理由是: 贝宝需要两位小数,因此,如果我有一款售价为49美元的产品,贝宝希望看到49.00美元的价格出现。Django的DecimalField()不设置十进制数。它只存储最大小数位数。所以,如果你有49在那里,你有字段设置为小数点后2位,它仍然会存储为49。我知道Django从数据库反序列化回十进制时基本上是类型转换(因为数据库没有十进制字段),所以我不太关心速度问题,也

我在这里遇到了一个范例问题。我不知道是应该将钱存储为Decimal(),还是应该将其存储为字符串并自己将其转换为十进制。我的理由是:

贝宝需要两位小数,因此,如果我有一款售价为49美元的产品,贝宝希望看到49.00美元的价格出现。Django的DecimalField()不设置十进制数。它只存储最大小数位数。所以,如果你有49在那里,你有字段设置为小数点后2位,它仍然会存储为49。我知道Django从数据库反序列化回十进制时基本上是类型转换(因为数据库没有十进制字段),所以我不太关心速度问题,也不太关心这个问题的设计问题。我想做对扩展性最好的事情


或者,更好的是,是否有人知道如何将django DecimalField()配置为始终使用两位格式样式进行格式化。

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

pricestr = "%01.2f" % price
如果需要,可以向模型中添加方法:

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

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

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


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

您将其存储为小数字段,并根据需要手动添加小数,正如Valya所说,使用基本的格式化技术


您甚至可以向产品或交易模型中添加一个模型方法,该方法将以适当格式的字符串形式显示十进制字段。

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

>>> from decimal import Decimal
>>> Decimal("12.234").quantize(Decimal("0.00"))
Decimal("12.23")
它还可以使用参数指定您想要的舍入方法(不同的会计系统可能需要不同的舍入)。更多信息请访问

下面是自动生成正确值的自定义字段。请注意,这仅在从数据库中检索时才起作用,并且在您自己进行设置时(直到您将其保存到数据库中并再次检索!)不会对您有所帮助

[编辑]


添加了
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu。因为货币是二维的价值(数量、货币)

pythonmoneylib,它有很多叉子,但我还没有找到一个可用的叉子


建议:

巨蟒货币可能是最好的叉子


django money由akumria推荐:(我还没有尝试过那种货币)。

根据我的经验,也是从我的经验来看,货币最好以货币和美分的形式组合存储


它非常容易处理和计算。

基于@Will\u Hardy的答案,这里就是它,这样您就不必每次都指定最大数字和小数点:

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

我的晚到派对版本增加了南方移民

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

我是Django的第一手,也是存储货币价值的第一手

存储货币值还有另一种可能性:只需将货币值转换为其次要表示形式,并将该整数值存储在数据库中。如果db后端的舍入可以根据货币算术需要进行调整,那么这应该是可行的

当检索一个值时,只要读回它并根据需要将其表示为字符串,这通常需要在将整数转换为字符串后在正确的位置放置一个十进制分隔符


看起来有些中间件确实期望这样的行为。

另外:在数据库中存储货币和金额。金钱不仅仅是一个数字,这是真的。感谢Shinny先生和NewShinny先生,如果您知道所有货币都将是相同的,那么您当然不需要在数据库中存储货币。事实上,大多数应用程序不处理多种货币。非常好。谢谢你。这就是拥有一个功能齐全的自定义模型字段所需要做的全部工作吗?(也就是说,除了格式问题外,这与DecimalField的工作原理是否完全相同)。另外,存储自定义字段的约定是什么?我想我会把它放在根项目文件夹里。谢谢,我刚刚用过这个。我注意到我每次使用CurrencyField时都必须重复max_digits和decimal_places属性,因此我发布了一个基于您的答案来解决这个问题。我希望您能解释一下
量化的基本原理,因为您的答案和python文档都没有明确说明这个用法。好的,我添加了一个关于
.quantize()
re“因为数据库没有十进制字段”的快速解释;Microsoft Sql Server既有“decimal”数据类型也有“money”数据类型我有一个类似的字段,但我使用了
defaults={…
defaults.update(**kwargs)
模式。路径是否在“['^application\.fields\.CurrencyField']”中必须根据我的项目进行更改吗?ThxBe意识到并非每种货币的1分(例如美分)等于0.01分(美元)。我不应该使用“美分”一词,但更多的是该货币的最小且不可进一步分割的单位。不过,这样你的应用程序就不会同时支持主辅币比率不同的货币。@ElmoVanKielmo你能举一个这样一种货币的例子吗?假设一种货币的主辅币为1000(1minor=0.001major),可以将货币精度存储为元数据。@pjotr_dolphin Japanese currency,例如1 rin=0.001日元。您是对的,货币精度应该存储为元数据,因为它是最新的,通过PIP进行维护和安装。尝试使用后,我会警告您,它并没有真正维护(我的问题是你和
ma
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'])