什么是最好的Django模型字段来表示美元金额?

143

我需要在Django模型的字段中存储美元$金额。最好的模型字段类型是什么?我需要让用户输入这个值(进行错误检查,只想要精确到分的数字),将其格式化以在不同位置向用户输出,并用它来计算其他数字。

6个回答

253

167
除非您想代表国家债务,否则 max_digits 必须大于 20。 - Bron Davies
5
如果需要支持其他货币,decimal_places=2 不一定是正确的。一些货币有三个小数位,比特币甚至有8个惊人的小数位。 - Lie Ryan
3
我认为这个例子几乎适用于所有货币。如果要将货币表示为比特币,最好使用整数字段以保存Satoshis的金额,然后以您想要的表示方式向最终用户展示它(BTC、mBTC等)。 - jion
@LieRyan,没错。我们必须注意现代“货币”类型,以防止未来的数据库修改。对于使用引号并不冒犯。 - enchance
@BronDavies 野蛮 - NoName
3
该回答建议在使用十进制列存储货币时,将最大位数设置为19,小数点后保留4位以确保安全。 - user3064538

55

其他答案都是100%正确的,但不太实用,因为您仍然需要手动管理输出、格式等。

我建议使用django-money

from djmoney.models.fields import MoneyField
from django.db import models


def SomeModel(models.Model):
    some_currency = MoneyField(
        decimal_places=2,
        default=0,
        default_currency='USD',
        max_digits=11,
    )

自动从模板中工作:

{{ somemodel.some_currency }}

输出:

$123.00

它通过python-money拥有强大的后端,并且本质上是标准小数字段的即插即用替代品。


(带有错误检查,只需要精确到分的数字),将其格式化输出给不同地方的用户,并用它计算其他数字。对于这种情况,使用DecimalField更实用。我相信大多数人都会使用它。正如它们所强调的那样,使用django-money ,包括货币处理。因此,这更适用于涉及交易的项目,例如电子商务网站、支付网关、数字货币、银行等... - Yeo
我认为,由于他们正在使用美元这种货币,因此更合理的做法是使用像django-money(python-money)这样的货币处理库,因为它可以使所有东西按照您的期望工作,并提供未来的功能调整。原始问题说他们需要格式化和计算(正如您引用的那样),为此,最好使用货币库而不是简单的十进制字段。 - Michael Thompson
1
如果我想要添加两个货币字段或进行一些计算,该怎么办? - saran3h
2
@saran3h,您可以使用货币实例执行与任何其他数字(十进制)实例相同的计算。您可以进行加法/减法、整数乘法等操作。 - Michael Thompson

45

8
定义一个十进制数,并在数值前加上美元符号。
    price = models.DecimalField(max_digits=8, decimal_places=2)

    @property
    def price_display(self):
        return "$%s" % self.price

8
你知道你在属性中创造了一个无限递归循环,对吗? - El Ninja Trepador
有趣。我可以问一下是怎么回事吗? - Redgren Grumbholdt

2
field = models.DecimalField(max_digits=8, decimal_places=2)

应该创建一个类似于以下的 PostgreSQL 字段:
 "field" numeric(8, 2) NOT NULL

如何在PostGreSQL中存储美元金额?

如果您需要一个PostgreSQL字段类型“double precision”,那么您需要在django模型中进行以下操作:

field = models.FloatField()

3
请注意不要将货币表示为浮点数,因为人们往往会对与其货币相关的四舍五入误差感到不满。详情请参见:https://dev59.com/h2865IYBdhLWcg3wnP2a。 - Adam Parkin

1
你可以使用django-money来处理money字段:
首先,按照下面的示例安装django-money[exchange]
pip install django-money[exchange]

*您可以按照下面的方式安装django-money,但是它没有转换货币的功能,所以我建议按照上面的方式安装django-money[exchange]
pip install django-money

接下来,在`core/settings.py`中的`INSTALLED_APPS`中添加`'djmoney.contrib.exchange'`:
# "core/settings.py"

INSTALLED_APPS = [
    ...,
    'djmoney.contrib.exchange',
]

然后,运行以下命令:
python manage.py migrate

然后,在`my_app/models.py`中的`MyModel`模型中,使用MoneyField()MinMoneyValidator()MaxMoneyValidator()Decimal定义一个字段,如下所示:
# "my_app/models.py"

from djmoney.models.fields import MoneyField
from decimal import Decimal
from djmoney.models.validators import MaxMoneyValidator, MinMoneyValidator

class MyModel(models.Model):
    money = MoneyField(
        max_digits=5, decimal_places=2, default=0, default_currency='USD',
        validators=[
            MinMoneyValidator(Decimal(0.00)), MaxMoneyValidator(Decimal(999.99)),
        ]
    )

然后,运行以下命令:
python manage.py makemigrations && python manage.py migrate

然后,您可以在Django管理界面上添加一个值,如下所示:

enter image description here

然后,您可以使用$获取带有$的值,并在my_app/views.py中使用.amount获取不带$的值,如下所示:
# "my_app/views.py"

from django.http import HttpResponse
from app.models import MyModel

def test(request):
    print(MyModel.objects.all()[0].money) # Here
    print(MyModel.objects.all()[0].money.amount) # Here
    return HttpResponse("Test")

然后,这些在控制台上显示:

$12.54
12.54

接下来,您可以将 12.54 USD 转换为 ... EUR

首先,打开Open Exchange Rates,然后注册以获取货币的汇率:

enter image description here

然后,从您的仪表板复制应用程序ID

enter image description here

然后,在core/settings.py中使用App ID设置OPEN_EXCHANGE_RATES_APP_ID
# "core/settings.py"
                             # Here
OPEN_EXCHANGE_RATES_APP_ID = '368183b0b2644e999ef2a61bd38d0ca3'

然后,运行以下命令:
python manage.py update_rates

然后,您可以使用下面的convert_money()Money()12.54 USD转换为... EUR。*您必须使用不带$的值,使用.amount
# "my_app/views.py"

from django.http import HttpResponse
from app.models import MyModel
from djmoney.contrib.exchange.models import convert_money
from djmoney.money import Money

def test(request):
    money = MyModel.objects.all()[0].money.amount
    print(convert_money(Money(money, 'USD'), 'EUR')) # Here
    return HttpResponse("Test")

然后,12.54美元被转换为11.70欧元如下所示:
€11.70

你可以查看更详细的信息,请参阅django-money文档

此外,你可以使用core/tasks.pycore/settings.py中的celery beat代码每60分钟更新货币的汇率。*Open Exchange Rates在免费计划中每月可接受1000个请求:

# "core/tasks.py"

from celery import shared_task
from djmoney import settings
from django.utils.module_loading import import_string

@shared_task
def update_rates(app_id):
    backend = import_string(settings.EXCHANGE_BACKEND)(
        access_key=app_id
    )
    backend.update_rates()
    print("Successfully updated")

*您应将 OPEN_EXCHANGE_RATES_APP_ID 放在 settings.py 中的 celery beat 代码之前,并导入 appcrontabupdate_rates,如下所示:

# "core/settings.py"

OPEN_EXCHANGE_RATES_APP_ID = '368183b0b2644e999ef2a61bd38d0ca3'

from .celery import app
from celery.schedules import crontab
from .tasks import update_rates

@app.on_after_configure.connect
def setup_periodic_tasks(sender, **kwargs):
    sender.add_periodic_task(
        crontab(minute='*/60'),
        update_rates.s(OPEN_EXCHANGE_RATES_APP_ID),
        name='update_rates'
    )

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接