3. Тип Money

12 октября 2016 г. 9:09

До djangoSHOP версии 0.2, суммы, относящиеся к деньгам, хранились как десятичный тип и хранились в модели базы данных с помощью DecimalField. Валюта в этом случае была только одна, и это обстоятельство не является серьезной проблемой, если вы предполагаете продажу товаров в одной валюте, а сам символ валюты может быть жестко закодирован в любом месте на сайте.

Тем не менее, для сайтов, предлагающих информацию о ценах более чем в одной валюте, это вызвало серьезные проблемы. Когда нам нужно было производить вычисления с суммами, которые имеют соответствующие валюты, очень часто допускались ошибки путем смешивания различных валют. Также, возможно, могли быть неправильные преобразования валют, которые генерировали неправильные результаты. Python не позволяет разработчикам связать конкретное значение десятичного с юнитом.

Начиная с версии 0.3.0, djangoSHOP теперь поставляется со специальным классом фабрики:

3.1. MoneyMaker

MoneyMaker - это фабрика для создания денежного типа с соответствующей валюты (этот класс не может быть реализован). Он использует хорошо изученный Decimal для совершения операций над количеством. Кроме того, он ограничивает операции на текущем типе денег. Например, мы не можем сложить доллары с евро. Мы также не можем умножать две валюты друг с другом.

3.1.1. Не число

В особых случаях мы можем хотеть указать "бесплатно", а не количество 0,00 (ноль). Это может пригодиться для бесплатных товаров или, когда товара сейчас нет в наличии. Тип Decimal обозначает особое значение NaN - "не число". Наш тип Money также знает о значении валюты и при рендере выведет €.

Объявление объекта Money без стоимости, скажем, m = Money() создает такую ​​особое значение. Большая разница состоит в том, что для Decimal при добавлении или вычитании NaN допустимое значение и считается нулевым, то есть не изменяется результат этой операции на NaN.

Также мы можем умножать сумму денег с None. Результат умножение будет NaN.

3.1.2. Создание типа Money

>>> from shop.money_maker import MoneyMaker
>>> Money = MoneyMaker()
>>> print Money('1.99')
€ 1.99

>>> print Money('1.55') + Money('8')
€ 9.55

>>> print Money
<class 'shop.money.money_maker.MoneyInEUR'>

>>> Yen = MoneyMaker('JPY')
>>> print Yen('1234.5678')
¥ 1235

>>> print Money('100') + Yen('1000')
ValueError: Can not add/substract money in different currencies.

Как это работает?

Вызывая MoneyMaker(), создаётся объект с валютой по умолчанию. Валюту по умолчанию можно изменить в settings.py переменной SHOP_DEFAULT_CURRENCY с использованием официальных валютных кодов, записанным в ISO-4217. Например, для доллара: SHOP_DEFAULT_CURRENCY = 'USD'.

Также мы можем создать наш собственный тип Money, например Yen.

3.1.3. Форматирование Money

Когда сумма денежного типа выводится на экран или сохраняется в переменную, используя str(price), то после значения суммы выводится соответствующий символ валюты. Это выглядит хорошо, когда работаешь только с несколькими валютами. Однако, некоторые символы, например, канадские, австралийские и доллары США, выводят сначала валюту “$”, а затем значение стоимости.

С помощью переменной SHOP_MONEY_FORMAT в settings.py мы можем стилизовать вывод денег. По умолчанию, выводится так: {symbol} {amount}. Доступное форматирование:

  • {symbol}: Короткий символ для валюты, например, $, £, €, ¥ и т. д.
  • {code}: Международный валютный код, например USD, GBP, EUR, JPY и т. д.
  • {currency}: Разговорное валютное описание, например “US Dollar”, “Pound Sterling” и т. д.
  • {amount}: нелокализованная сумма.

Таким образом, если мы хотим, чтобы выводилось 9.98 US Dollar, то мы должны установить {amount} {currency} в качестве строки форматирования.

3.2. Локализация денег

Так как класс Money не знает ничего о нашей текущей локали, суммы всегда будут выводиться нелокализованными. Для локализации тип Money используйте django.utils.numberformat.format(someamount). Эта функция вернёт локализированную сумму согласно текущему HTTP запросу.

3.3. Поле Money в базе данных

Деньги могут храниться в базе данных, храня валютную информацию вместе с типом поля. Изнутри это работает так: база данных использует тип Decimal, но также поля знает его валюту и будет возвращать стоимость как тип MoneyIn... Это предотвращает неявные и скрытые валютные преобразования.

Например, рассмотрим следующий пример:

class Product(models.Model):
    ...
    unit_price = MoneyField(currency='GBP')

Это поле хранит стоимость в фунтах стерлингов и возвращает его как тип MoneyInGBP. Если аргумент валюты явно не указан, то используется значение по умолчанию.

3.4. Представление денег в формате JSON

Дополнительный REST SerializerField может быть добавлен для конвертации суммы в JSON. При написании сериализатора используйте:

from rest_framework import serializers
from shop.money.rest import MoneyField

class SomeSerializer(serializers.ModelSerializer):
    price = MoneyField()

По умолчанию REST сериализует типы Decimal как Float. Это нормально, если мы хотим сделать некоторые вычисления в браузере с помощью JavaScript. Тем не менее, информация о валюте теряется и должна быть вновь добавлена как-то к выходным строкам. Кроме того, это плохая идея, если делать коммерческие вычисления с использованием Float, но JavaScript не предлагает Decimal-подобный тип. Поэтому я рекомендую всегда выполнять коммерческие вычисления на сервере, а затем отправлять полученную сумму с помощью JSON строки.

Оцените статью

0 из 5 (всего 0 оценок)

Поля, отмеченные звёздочкой ( * ) , являются обязательными.

Спасибо за ваш отзыв!

После нажатия кнопки "Отправить" ваше сообщение будет доставлено мне на почту.

Автор перевода

Права на использование материала, расположенного на этой странице http://vivazzi.pro/django-shop/money-types/:

Разрешается копировать материал с указанием её автора и ссылки на оригинал без использования параметра rel="nofollow" в теге <a>. Использование:

Автор перевода: Мальцев Артём
Ссылка на перевод статьи: <a href="http://vivazzi.pro/django-shop/money-types/">http://vivazzi.pro/django-shop/money-types/</a>

Подробнее: Правила использования сайта

Комментариев: 0

Вы можете оставить комментарий как незарегистрированный пользователь. Но, зарегистрировавшись, вы сможете получать оповещения об ответах, а также иметь доступ к своему личному аккаунту для просмотра своих комментариев.

Чтобы оставить комментарий от своего имени войдите или зарегистрируйтесь обычным способом или через социальные сети:

Отправить

На данный момент нет специального поиска, поэтому я предлагаю воспользоваться обычной поисковой системой, например, Google, добавив "vivazzi" после своего запроса.

Попробуйте