Django:我该如何存储货币值?
我遇到了一个思维方式的问题。我不确定应该把钱存储为Decimal(),还是把它存储为字符串,然后自己转换成小数。我的想法是这样的:
PayPal要求金额有两位小数,所以如果我有一个产品价格是49美元,PayPal希望看到的是49.00这个格式。Django的DecimalField()并不会设置小数的具体数值。它只是存储最多可以有多少位小数。所以,如果你存储的是49,并且这个字段设置为2位小数,它仍然会存储为49。我知道Django在从数据库反序列化回Decimal时,基本上是在进行类型转换(因为数据库没有小数字段),所以我并不太担心速度问题,更关注的是这个设计上的问题。我想做出最适合扩展性的选择。
或者,更好的是,有人知道怎么配置Django的DecimalField(),让它总是以两位小数的格式显示吗?
10 个回答
13
我这个晚来的版本添加了South迁移功能。
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'])
18
我觉得你应该把它存储为小数格式,然后在发送给PayPal之前,格式化成00.00的样子,像这样:
pricestr = "%01.2f" % price
如果你愿意的话,可以在你的模型里添加一个方法:
def formattedprice(self):
return "%01.2f" % self.price
65
你可能想用 .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: 为什么这个自定义模型字段的表现和预期不一样?