跳转到主要内容

添加了对在django模型和表单中使用货币和货币字段的支持。使用py-moneyed作为货币实现。

项目描述

Build Status Coverage Status Documentation Status PyPI

一个小型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-moneyeddjango-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=19decimal_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 支持的分支。

此版本添加了测试,并包含多个关键错误修复。

项目详情


发布历史 发布通知 | RSS 源

下载文件

下载适合您平台的文件。如果您不确定该选择哪个,请了解更多关于安装包的信息。

源分布

django_money-3.5.3.tar.gz (42.3 kB 查看哈希值)

上传时间:

构建分布

django_money-3.5.3-py3-none-any.whl (35.9 kB 查看哈希值)

上传时间: Python 3

AWS AWS 云计算和安全赞助商 Datadog Datadog 监控 Fastly Fastly CDN Google Google 下载分析 Microsoft Microsoft PSF赞助商 Pingdom Pingdom 监控 Sentry Sentry 错误日志 StatusPage StatusPage 状态页面