Django: Как мне хранить денежную стоимость?
Я столкнулся с проблемой парадигмы. Я не знаю, следует ли хранить деньги как Decimal(), или если я должен хранить его как строку и преобразовать ее в десятичный знак. Мое рассуждение таково:
PayPal требует 2 десятичных знака, поэтому, если у меня есть продукт, который равен 49 долларам, PayPal хочет видеть, что 49.00 встречается с проводом. Django DecimalField() не устанавливает десятичную сумму. Он хранит максимальное количество знаков после запятой. Итак, если у вас есть 49, и у вас есть поле, равное 2 десятичным разрядам, оно все равно сохранит его как 49. Я знаю, что Django - это, в основном, литье типов, когда он десериализует обратно из базы данных в десятичный (начиная с Базы данных не имеют десятичных полей), поэтому я не полностью обеспокоен проблемами скорости, так же как и с проблемами дизайна этой проблемы. Я хочу сделать то, что лучше подходит для расширяемости.
Или, еще лучше, кто-нибудь знает, как настроить django DecimalField(), чтобы всегда форматировать стиль форматирования TWO_PLACES.
Ответы
Ответ 1
Возможно, вы захотите использовать метод .quantize()
. Это будет округлять десятичное значение до определенного количества мест, аргумент, который вы указываете, указывает количество мест:
>>> from decimal import Decimal
>>> Decimal("12.234").quantize(Decimal("0.00"))
Decimal("12.23")
Он также может принять аргумент, чтобы указать, какой метод округления вы хотите (разные системы учета могут потребовать различного округления). Дополнительная информация в Документы Python.
Ниже настраивается поле, которое автоматически производит правильное значение. Обратите внимание, что это происходит только тогда, когда он извлекается из базы данных и не поможет вам, когда вы установите его самостоятельно (пока вы не сохраните его в db и не получите снова!).
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: Почему это пользовательское поле модели не ведет себя так, как ожидалось?
Ответ 2
Я думаю, вы должны сохранить его в десятичном формате и отформатировать его до формата 00.00 только, а затем отправить его в PayPal, например:
pricestr = "%01.2f" % price
Если вы хотите, вы можете добавить метод к своей модели:
def formattedprice(self):
return "%01.2f" % self.price
Ответ 3
Моя поздняя версия партии, которая добавляет южные миграции.
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'])
Ответ 4
Деньги должны храниться в денежном поле, чего, к сожалению, не существует. Так как деньги - двумерное значение (сумма, валюта).
Существует python-money lib, у которой много вилок, но я не нашел рабочего.
Рекомендации:
python-money, вероятно, лучший fork https://bitbucket.org/acoobe/python-money
django-money, рекомендованный akumria: http://pypi.python.org/pypi/django-money/ (havent попробовал еще один).
Ответ 5
Я предлагаю избежать смешивания представления с хранилищем. Храните данные в виде десятичного значения с двумя местами.
В слое пользовательского интерфейса отобразите его в форме, подходящей для пользователя (поэтому, возможно, опустите ".00" ).
Когда вы отправляете данные в PayPal, отформатируйте их по мере необходимости.
Ответ 6
Основываясь на ответе @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
Ответ 7
По моему опыту, а также из других, деньги лучше всего хранить в виде комбинации валюты и суммы в центах.
Он очень легко обрабатывается и вычисляется с ним.
Ответ 8
Вы храните его как DecimalField и вручную добавляете десятичные знаки, если вам нужно, как сказал Валя, использовать основные методы форматирования.
Вы даже можете добавить модельный метод к вашему продукту или модели транзакций, которая выплюнет DecimalField как соответствующую отформатированную строку.
Ответ 9
Я лично знакомлюсь с Django и храню денежные ценности.
Существует еще одна возможность хранить денежные значения: просто преобразовать денежную стоимость в ее младшее представление и сохранить это целое значение в базе данных. Если округление бэкэнда БД можно скорректировать в соответствии с потребностями денежной арифметики, это должно сработать.
При извлечении значения просто прочитайте его обратно и, при необходимости, представьте его в виде строки, что обычно предполагает размещение десятичного разделителя в нужной позиции после преобразования целого числа в строку.
Похоже, что некоторые промежуточные программы ожидают такого поведения . Преобразование десятичного числа в целое без потери денежного значения.