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 и храню денежные ценности.

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

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

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