添加了对在django模型和表单中使用货币和货币字段的支持。使用py-moneyed作为货币实现。
项目描述
一个小型Django应用程序,使用py-moneyed为您的模型和表单添加货币字段的支持。
支持的Django版本:2.2, 3.2, 4.0, 4.1, 4.2
支持的Python版本:3.7、3.8、3.9、3.10、3.11
支持的PyPy版本:PyPy3(适用于Django ≤ 4.0)
如果您需要支持旧版本的Django和Python,请参阅发行说明中提到的旧版本。
通过依赖项py-moneyed,django-money获得以下支持:
对正确的Money值处理的支持(使用标准的Money设计模式)
货币类以及所有流通货币的定义
大多数货币的正确货币符号格式化
安装
使用pip
$ pip install django-money
这将自动安装py-moneyed v1.2(或更高版本)。
将djmoney添加到您的INSTALLED_APPS中。这是在管理界面正确显示货币字段所必需的。
INSTALLED_APPS = [
...,
'djmoney',
...
]
模型使用
作为正常模型字段使用
from djmoney.models.fields import MoneyField
from django.db import models
class BankAccount(models.Model):
balance = MoneyField(max_digits=14, decimal_places=2, default_currency='USD')
为了符合某些严格的会计或金融法规,您可以考虑使用max_digits=19和decimal_places=4,更多内容请参阅此StackOverflow回答
也可以有一个可空的MoneyField
class BankAccount(models.Model):
money = MoneyField(max_digits=10, decimal_places=2, null=True, default_currency=None)
account = BankAccount.objects.create()
assert account.money is None
assert account.money_currency is None
搜索具有货币字段的模型
from djmoney.money import Money
account = BankAccount.objects.create(balance=Money(10, 'USD'))
swissAccount = BankAccount.objects.create(balance=Money(10, 'CHF'))
BankAccount.objects.filter(balance__gt=Money(1, 'USD'))
# Returns the "account" object
默认货币代码长度为3,但您可以通过CURRENCY_CODE_MAX_LENGTH设置来更改它。
注意:此设置也会影响exchange插件的初始迁移,因此初始迁移运行后更改此设置将没有效果。(您需要运行manage migrate exchange zero并再次迁移以更改它)。
字段验证
字段验证有3种不同的情况:
根据货币的数值部分进行验证,而不考虑货币本身;
根据单个货币金额进行验证;
根据多个货币金额进行验证。
所有这些都可以组合使用,如下所示
from django.db import models
from djmoney.models.fields import MoneyField
from djmoney.money import Money
from djmoney.models.validators import MaxMoneyValidator, MinMoneyValidator
class BankAccount(models.Model):
balance = MoneyField(
max_digits=10,
decimal_places=2,
validators=[
MinMoneyValidator(10),
MaxMoneyValidator(1500),
MinMoneyValidator(Money(500, 'NOK')),
MaxMoneyValidator(Money(900, 'NOK')),
MinMoneyValidator({'EUR': 100, 'USD': 50}),
MaxMoneyValidator({'EUR': 1000, 'USD': 500}),
]
)
上面模型的balance字段有以下验证:
所有输入值应介于10和1500之间,不考虑货币本身;
挪威克朗(NOK)金额应介于500和900之间;
欧元应介于100和1000之间;
美元应介于50和500之间;
构造表单数据
默认的ModelForm类将使用一个表单字段(djmoney.forms.fields.MoneyField),该字段由两个单独的字段组成,一个是金额,另一个是货币。
如果您需要直接向此类表单提供数据(例如,如果您正在编写测试用例),则需要像这样传递金额和货币:
# models.py
class Product(models.Model):
price = MoneyField(
max_digits=14,
decimal_places=2,
default_currency='EUR'
)
# forms.py
class ProductForm(ModelForm):
class Meta:
model = Product
fields = ["price"]
# tests.py
# construct the form in your test case
form = ProductForm({'price_0': 10, 'price_1': 'EUR'})
添加新货币
货币列在moneyed上,此模块使用它为admin提供选择列表,也用于验证。
要添加所有项目都可用的新货币,您只需将以下几行添加到您的settings.py文件中
import moneyed
BOB = moneyed.add_currency(
code='BOB',
numeric='068',
name='Peso boliviano',
countries=('BOLIVIA', )
)
要限制项目上列出的货币,请在settings.py中设置一个包含货币代码列表的CURRENCIES变量
CURRENCIES = ('USD', 'BOB')
该列表必须包含有效的货币代码
此外,还可以直接指定货币选择
CURRENCIES = ('USD', 'EUR')
CURRENCY_CHOICES = [('USD', 'USD $'), ('EUR', 'EUR €')]
关于模型管理器的重要说明
Django-money允许您为模型使用任何您喜欢的自定义模型管理器,但它需要包装一些方法,以便能够搜索具有货币值的模型。
对于任何使用MoneyField的模型,"objects"属性将自动进行此操作。但是,如果您将管理器分配给某些其他属性,则必须手动包装您的管理器,如下所示
from djmoney.models.managers import money_manager
class BankAccount(models.Model):
balance = MoneyField(max_digits=10, decimal_places=2, default_currency='USD')
accounts = money_manager(MyCustomManager())
此外,money_manager包装器仅包装标准QuerySet方法。如果您定义了自定义QuerySet方法,这些方法最终没有使用任何标准方法(如“get”、“filter”等),那么您还需要手动装饰这些自定义方法,如下所示
from djmoney.models.managers import understands_money
class MyCustomQuerySet(QuerySet):
@understands_money
def my_custom_method(*args, **kwargs):
# Awesome stuff
关于序列化的说明
Django-money提供了一个自定义反序列化器,默认情况下没有注册,因此您需要在您的settings.py
中主动注册它。
SERIALIZATION_MODULES = {"json": "djmoney.serializers"}
格式本地化
如果您在设置文件中设置了USE_L10N = True
,则格式会被启用。
如果配置中禁用了格式,则模板将使用默认格式。
在模板中,您可以使用特殊标签来格式化货币。
在settings.py
文件中,将库djmoney
的条目添加到INSTALLED_APPS
。
INSTALLED_APPS += ('djmoney', )
在模板中添加
{% load djmoney %} ... {% money_localize money %}
这就是全部了。
关于标签money_localize
的说明
{% money_localize <money_object> [ on(default) | off ] [as var_name] %} {% money_localize <amount> <currency> [ on(default) | off ] [as var_name] %}
示例
相同的效果
{% money_localize money_object %} {% money_localize money_object on %}
变量赋值
{% money_localize money_object on as NEW_MONEY_OBJECT %}
使用货币格式化数字
{% money_localize '4.5' 'USD' %}
Return:: Money object
测试
安装所需的包
git clone https://github.com/django-money/django-money cd ./django-money/ pip install -e ".[test]" # installation with required packages for testing
运行测试的推荐方式
tox
在当前Python环境中测试应用程序
make test
处理汇率
要处理汇率,请将以下内容添加到您的INSTALLED_APPS
中。
INSTALLED_APPS = [
...,
'djmoney.contrib.exchange',
]
此外,还需要安装certifi
。可以通过安装带有exchange
附加组件的djmoney
来实现。
pip install "django-money[exchange]"
要创建所需的关联,请运行python manage.py migrate
。要填充这些关联的数据,您需要选择一个数据源。目前支持两种数据源 - https://openexchangerates.org/(默认)和https://fixer.io/。要选择另一个数据源,请设置EXCHANGE_BACKEND
设置,将其导入为所需的后端字符串。
EXCHANGE_BACKEND = 'djmoney.contrib.exchange.backends.FixerBackend'
如果您想实现自己的后端,需要扩展djmoney.contrib.exchange.backends.base.BaseExchangeBackend
。上面提到的两种数据源都不是公开的,因此您必须指定访问密钥才能使用它们。
OPEN_EXCHANGE_RATES_APP_ID
- '<您的实际密钥来自openexchangerates.org>
'
FIXER_ACCESS_KEY
- '<您的实际密钥来自fixer.io>
'
后端返回基本货币的汇率,默认为USD,但可以通过BASE_CURRENCY
设置进行更改。Open Exchanger Rates & Fixer支持一些额外功能,如历史数据或限制响应中的货币列表。要使用这些功能,您可以更改这些后端的默认URL。
OPEN_EXCHANGE_RATES_URL = 'https://openexchangerates.org/api/historical/2017-01-01.json?symbols=EUR,NOK,SEK,CZK'
FIXER_URL = 'http://data.fixer.io/api/2013-12-24?symbols=EUR,NOK,SEK,CZK'
或者,您可以直接传递给update_rates
方法。
>>> from djmoney.contrib.exchange.backends import OpenExchangeRatesBackend
>>> backend = OpenExchangeRatesBackend(url='https://openexchangerates.org/api/historical/2017-01-01.json')
>>> backend.update_rates(symbols='EUR,NOK,SEK,CZK')
可以同时使用多个后端。
>>> from djmoney.contrib.exchange.backends import FixerBackend, OpenExchangeRatesBackend
>>> from djmoney.contrib.exchange.models import get_rate
>>> OpenExchangeRatesBackend().update_rates()
>>> FixerBackend().update_rates()
>>> get_rate('USD', 'EUR', backend=OpenExchangeRatesBackend.name)
>>> get_rate('USD', 'EUR', backend=FixerBackend.name)
常规操作中的Money
将使用EXCHANGE_BACKEND
后端获取汇率。此外,还有两个管理命令用于更新和删除汇率。
$ python manage.py update_rates
Successfully updated rates from openexchangerates.org
$ python manage.py clear_rates
Successfully cleared rates for openexchangerates.org
这两个命令都接受-b/--backend
选项,它将只更新/清除此后端的数据。并且clear_rates
接受-a/--all
选项,它将清除所有后端的数据。
要设置定期更新汇率,可以使用Celery任务。
CELERY_BEAT_SCHEDULE = {
'update_rates': {
'task': 'path.to.your.task',
'schedule': crontab(minute=0, hour=0),
'kwargs': {} # For custom arguments
}
}
示例任务实现
from django.utils.module_loading import import_string
from celery import Celery
from djmoney import settings
app = Celery('tasks', broker='pyamqp://guest@localhost//')
@app.task
def update_rates(backend=settings.EXCHANGE_BACKEND, **kwargs):
backend = import_string(backend)()
backend.update_rates(**kwargs)
将一种货币转换为另一种货币
>>> from djmoney.money import Money
>>> from djmoney.contrib.exchange.models import convert_money
>>> convert_money(Money(100, 'EUR'), 'USD')
<Money: 122.8184375038380800 USD>
汇率与Django Admin集成。
django-money 可以通过设置 Django 中的 AUTO_CONVERT_MONEY = True 来自动使用此应用程序进行货币转换。请注意,货币转换是一个有损过程,因此自动转换通常只适用于非常简单的用例。对于大多数用例,您需要明确货币转换发生的时间,自动转换可能会隐藏错误。此外,由于转换方向不同,自动转换会使您失去一些属性,如交换律(A + B == B + A)。
与 Django REST Framework 一起使用
请确保 djmoney 已添加到您的 settings.py 文件中的 INSTALLED_APPS,并且已安装 rest_framework。MoneyField 会通过 djmoney.apps.MoneyConfig.ready() 自动为 Django REST Framework 注册序列化器。
您可以按照以下方式添加可序列化的字段
from djmoney.contrib.django_rest_framework import MoneyField
class Serializers(serializers.Serializer):
my_computed_prop = MoneyField(max_digits=10, decimal_places=2)
内置序列化器按以下方式工作
class Expenses(models.Model):
amount = MoneyField(max_digits=10, decimal_places=2)
class Serializer(serializers.ModelSerializer):
class Meta:
model = Expenses
fields = '__all__'
>>> instance = Expenses.objects.create(amount=Money(10, 'EUR'))
>>> serializer = Serializer(instance=instance)
>>> serializer.data
ReturnDict([
('id', 1),
('amount_currency', 'EUR'),
('amount', '10.000'),
])
请注意,当在序列化器中指定单个字段时,金额和货币字段是分开处理的。要实现上述相同的操作,您需要包含两个字段名
class Serializer(serializers.ModelSerializer):
class Meta:
model = Expenses
fields = ('id', 'amount', 'amount_currency')
自定义
如果需要自定义将 Money 实例分解到 Django 字段的过程以及反向操作,则可以使用如下自定义描述符
class MyMoneyDescriptor:
def __get__(self, obj, type=None):
amount = obj.__dict__[self.field.name]
return Money(amount, "EUR")
它将在调用 obj.money 时始终为所有 Money 实例使用 EUR。然后应该将其传递给 MoneyField
class Expenses(models.Model):
amount = MoneyField(max_digits=10, decimal_places=2, money_descriptor_class=MyMoneyDescriptor)
背景
此项目是 http://code.google.com/p/python-money/ 中的 Django 支持的分支。
此版本添加了测试,并包含多个关键错误修复。
项目详情
下载文件
下载适合您平台的文件。如果您不确定该选择哪个,请了解更多关于安装包的信息。
源分布
构建分布
django_money-3.5.3.tar.gz的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | cb8ef1adea8c682b792cc565ace45ace9525de26e3b116290951cd78c7393eab |
|
MD5 | e435938468c67569aadf31ceb8351ea2 |
|
BLAKE2b-256 | 988ca1f63e1b94f477d6f28b890c15f5352f9ecd6861e60d7e7dd4b8a6f88634 |
django_money-3.5.3-py3-none-any.whl的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 3020c0f6b77eb4c30bdd711c7c660af67ed4b7a4750fdbdf5894788848dc6fc6 |
|
MD5 | ae65c6e3f05b38088f5f637456834584 |
|
BLAKE2b-256 | 32055712bb009945cb8e15ca340598707782b48db65bdffc57e042a0dce19914 |