您可能希望使用.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:为什么这个自定义模型字段的行为不如预期?
quantize
的原理,因为你的回答和 Python 文档都没有让我清楚地了解它的用途。 - user67416.quantize()
添加了一个快速说明。 - Will Hardy我认为你应该将它存储在十进制格式中,并将其格式化为00.00格式,仅当将其发送到PayPal时,像这样:
pricestr = "%01.2f" % price
如果你想的话,可以向你的模型中添加一个方法:
def formattedprice(self):
return "%01.2f" % self.price
我晚到派对的版本,增加了 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'])
货币应该存储在货币字段中,可惜它并不存在。因为货币是二维值(金额、货币类型)。
有一个名为python-money的库,有许多分支,但我还没有找到可用的分支。
建议:
python-money可能是最好的分支:https://bitbucket.org/acoobe/python-money
django-money被akumria推荐:http://pypi.python.org/pypi/django-money/(我还没有尝试过这个)。
我建议避免将表示与存储混合在一起。将数据作为带有2个小数位的十进制值进行存储。
在用户界面层,以适合用户的形式显示它(也许省略".00")。
当您将数据发送到PayPal时,请按照接口要求格式化它。
在@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
defaults = {...
和 defaults.update(**kwargs)
模式。 - orokusaki从我的经验和其他人的经验来看,最好将货币存储为货币和以分为单位的金额的组合。
这样非常容易处理和计算。
你可以将它存储为 DecimalField,并在需要时手动添加小数,就像 Valya 所说的那样,使用基本的格式化技术。
你甚至可以在你的产品或交易模型中添加一个模型方法,将 DecimalField 输出为适当格式的字符串。
我是Django编程的第一线实践者,也涉及存储货币价值。
另外一种存储货币价值的方式是:将货币价值转换为其较小的表示形式,并将整数值存储在数据库中。如果数据库后端的舍入可调整为货币算术需求,则应该可以使用此方法。
在检索值时,只需将其读回并根据需要表示为字符串,通常是在将整数转换为字符串后在正确位置放置十进制分隔符。
看起来某些中间件恰好期望这种行为在不失去货币价值的情况下将小数转换为整数。
import valuta
valuta.EUR.convert_to_currency_units(1_000) # 1_000 is the price in cents
# 10.0
使用(ISO-4217)货币代码的字符串表示进行编程
from valuta.shortcuts import convert_to_currency_units
convert_to_currency_units("EUR", 1_000) # 1_000 is the price in cents
# 10.0
Django集成
模型定义
from django.db import models
from valuta.contrib.django_integration.models import CurrencyField
class Product(models.Model):
price = models.IntegerField() # Amount in minor currency units
price_with_tax = models.IntegerField() # Amount in minor currency units
currency = CurrencyField(amount_fields=["price", "price_with_tax"])
样本数据
import valuta
from product.models import Product
product = Product.objects.create(
price=1_000, # Price in cents
price_with_tax=1_200, # Price in cents
currency=valuta.EUR.uid,
)
product.price_in_currency_units() # Price in euros
# 10.0
product.price_with_tax_in_currency_units() # Price in euros
# 12.0
product.price_display_in_currency_units() # Price in euros
# '10.00'
product.price_with_tax_display_in_currency_units() # Price in euros
# '12.00'
现在尝试另一种货币(JPY)。
product_jpy = Product.objects.create(
price=1_000, # Price in cents
price_with_tax=1_200, # Price in cents
currency=valuta.JPY.uid,
)
product.price_in_currency_units() # Price in JPY
# 10.0
product.price_with_tax_in_currency_units() # Price in JPY
# 12.0
product.price_display_in_currency_units() # Price in JPY
# '10'
product.price_with_tax_display_in_currency_units() # Price in JPY
# '12'