Django: как я должен хранить денежное значение?

Я столкнулся с проблемой парадигмы здесь. Я не знаю, должен ли я хранить деньги как Decimal (), или я должен хранить их как строку и преобразовывать ее в decimal. Мои рассуждения таковы:--1-->

PayPal требует 2 десятичных знаков, поэтому, если у меня есть продукт, который равен 49 долларам, PayPal хочет увидеть 49.00. DecimalField () Django не устанавливает десятичную сумму. Он хранит только максимальное количество десятичных знаков. Итак, если у вас есть 49 в там, и у вас есть поле, установленное на 2 десятичных знака, оно все равно будет хранить его как 49. Я знаю, что Django-это в основном литье типа, когда он десериализуется из базы данных в десятичное число (поскольку базы данных не имеют десятичных полей), поэтому меня не полностью волнуют проблемы скорости, так как я занимаюсь проблемами дизайна этой проблемы. Я хочу сделать то, что лучше для расширяемости.

или, еще лучше, кто-нибудь знает, как настроить Django DecimalField (), чтобы всегда форматировать с стиль форматирования TWO_PLACES.

8 ответов


вы можете использовать .quantize() метод. Это округлит десятичное значение до определенного количества мест, аргумент, который вы предоставляете, указывает количество мест:

>>> from decimal import Decimal
>>> Decimal("12.234").quantize(Decimal("0.00"))
Decimal("12.23")

также может потребоваться аргумент, чтобы указать, какой подход округления вы хотите (различные системы учета могут хотеть другого округления). Дополнительная информация в Python docs.

ниже пользовательское поле, которое автоматически создает правильное значение. Обратите внимание, что это только тогда, когда извлекается из базы данных и не поможет вам, когда вы установите его самостоятельно (пока вы не сохраните его в БД и не получите его снова!).

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

[edit]

добавил __metaclass__ см. Django: почему это настраиваемое поле модели ведет себя не так, как ожидалось?


Я думаю, вы должны сохранить его в десятичном формате и форматировать его в формате 00.00 только затем отправьте его в PayPal, например:

pricestr = "%01.2f" % price

Если вы хотите, вы можете добавить метод в модель:

def formattedprice(self):
    return "%01.2f" % self.price

мой конец к версии партии, которая добавляет южные миграции.

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'])

деньги должны храниться в денежном поле, которого, к сожалению, не существует. Поскольку деньги-это двумерное значение (сумма, валюта).

здесь python-деньги lib, у этого много вилок, но я не нашел ни одной рабочей.


рекомендации:

python-деньги вероятно, лучшая вилка https://bitbucket.org/acoobe/python-money

Джанго-деньги рекомендовано akumria: http://pypi.python.org/pypi/django-money/ (еще не пробовал).


Я предлагаю избегать смешивания представления с хранением. Сохраните данные в виде десятичного значения с 2 местами.

в слое пользовательского интерфейса отобразите его в форме, подходящей для пользователя (поэтому, возможно, опустите ".00").

когда вы отправляете данные в PayPal, форматируйте его так, как требует интерфейс.


основываясь на ответе @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

по моему опыту, а также из другие, деньги лучше всего хранить в виде комбинации валюты и суммы в центах.

очень легко отрегулировать и высчитать с ним.


вы храните его как десятичное поле и вручную добавляете десятичные знаки, Если вам нужно, как сказала Валя, используя основные методы форматирования.

вы даже можете добавить метод модели к вам продукт или модель транзакции, которая будет выплевывать десятичное поле как соответствующим образом отформатированную строку.