Django: как я должен хранить денежную ценность?


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

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

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

8 62

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-money lib, у которого много вилок, но я не нашел ни одной рабочей.


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

python-money наверное лучшая вилка 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

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

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

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

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