web-dev-qa-db-fra.com

Django: FloatField ou DecimalField pour la devise?

Je suis curieux de savoir lequel serait le mieux adapté en tant que champ monétaire? Je ferai des opérations simples comme prendre la différence, le pourcentage entre l'ancien et le nouveau prix. Je prévois de garder deux chiffres après le zéro (c.-à-d. 10,50) et la majorité du temps si ces chiffres sont nuls, je cacherai ces chiffres et les afficherai comme "10"

ps: la devise n'est pas basée sur le dollar :)

64
Hellnar

Utilisez toujours DecimalField pour de l'argent. Même les opérations simples (addition, soustraction) ne sont pas à l'abri des problèmes d'arrondi flottant:

>>> 10.50 - 0.20
10.300000000000001

>>> Decimal('10.50') - Decimal('0.20')
Decimal('10.30')
112
Seth

La réponse à la question est correcte, mais certains utilisateurs tomberont sur cette question pour découvrir la différence entre le DecimalField et le FloatField. Le problème de l'arrondi des flotteurs que Seth soulève est un problème pour la monnaie.

Le Django Docs States

La classe FloatField est parfois mélangée avec la classe DecimalField. Bien qu'ils représentent tous les deux des nombres réels, ils représentent ces nombres différemment. FloatField utilise le type float de Python en interne, tandis que DecimalField utilise le type Decimal de Python. En savoir plus ici .

Voici d'autres différences entre les deux champs:

DecimalField:

  • DecimalFields doit définir un 'decimal_places' et un attribut 'max_digits'.
  • Vous obtenez deux validations de formulaire gratuites incluses ici à partir des attributs requis ci-dessus, c'est-à-dire si vous définissez max_digits à 4 et que vous tapez une décimale qui est 4,00000 (5 chiffres), vous obtiendrez cette erreur: Assurez-vous qu'il n'y a pas plus de 4 chiffres au total.
  • Vous obtenez également une validation de formulaire similaire pour les décimales (qui, dans la plupart des navigateurs, validera également sur le front-end en utilisant l'attribut step dans le champ de saisie. Si vous définissez decimal_places = 1 et tapez 0,001 comme valeur, vous obtiendrez une erreur que la valeur minimale doit être de 0,1.
  • Renvoie un decimal.Decimal, le type est
  • N'a pas la validation supplémentaire comme DecimalField
  • Avec un type décimal, l'arrondi est également géré pour vous en raison des attributs requis qui doivent être définis comme décrit ci-dessus. Donc, du Shell, si vous
  • Dans la base de données (postgresql), le DecimalField est enregistré en tant que type numérique (max_digits, decimal_laces) et le stockage est défini comme "principal", à partir de l'exemple ci-dessus, le Type est numérique (4,1)

En savoir plus sur DecimalField de la Django Docs .

FloatField:

  • Renvoie le type flottant intégré,
  • Aucun arrondi intelligent et peut en fait entraîner des problèmes d'arrondi comme décrit dans la réponse de Seths.
  • N'a pas la validation de formulaire supplémentaire que vous obtenez de DecimalField
  • Dans la base de données (postgresql), le FloatField est enregistré en tant que type "double précision" et le stockage est défini comme "ordinaire"

En savoir plus sur FloatField du Django Docs .

S'applique aux deux:

  • Les deux champs s'étendent de la classe "Field" et peuvent accepter 'blank', 'null', 'verbose_name', 'name', 'primary_key', 'max_length', 'unique', 'db_index', 'rel', 'default ',' modifiable ',' sérialiser ',' unique_pour_date ',' unique_pour_mois ',' unique_pour_année ',' choix ',' help_text ',' db_column ',' db_tablespace ',' auto_created ',' validators ',' error_messages 'attributs , comme tous les champs qui s'étendent de "Field".
  • Le widget de formulaire par défaut pour les deux champs est un TextInput.

Je suis tombé sur cette question en cherchant la différence entre les deux domaines, donc je pense que cela aidera ceux qui sont dans la même situation :)

MISE À JOUR: Pour répondre à la question, je pense que vous pouvez vous en tirer pour représenter la monnaie, bien que Decimal soit un bien meilleur choix. Il y a un problème d'arrondi quand il compte pour les flottants, vous devez donc utiliser round(value, 2) afin de garder votre représentation flottante arrondie à deux décimales. Voici un petit exemple:

>>> round(1.13 * 50 + .01, 2)
56.51

Vous pouvez toujours avoir des ennuis avec le flotteur et le rond. Comme ici, nous le voyons arrondir à une valeur de 5:

>>> round(5.685, 2)
5.68

Mais dans ce cas, il arrondira:

>>> round(2.995, 2)
3.0

Cela a tout à voir avec la façon dont le flotteur est stocké en mémoire. Voir ici .

46
radtek

edit: Le projet Satchmo n'est plus actif, alors jetez un œil à ces alternatives pour gérer les devises


Basé sur Django Satchmo Project a un CurrencyField et CurrencyWidget qui valent le coup d'œil.

Consultez le répertoire de l'application satchmo_utils pour la source

6
sxalexander

Je sais que c'est super ancien, mais je suis tombé dessus à la recherche de quelque chose de complètement différent, et je voulais dire qu'il est généralement déconseillé d'utiliser des nombres à virgule flottante (flottant o décimal) pour la monnaie , car l'arrondi mathématique à virgule flottante entraînera invariablement de petites erreurs de calcul pouvant entraîner de très grandes différences dans le temps.

Utilisez plutôt un champ entier ou une chaîne à votre convenance. Multipliez votre devise pour déplacer la décimale à la fin et faites un nombre entier lorsque vous la stockez, puis replacez cette décimale à sa place lorsque vous devez l'afficher. C'est essentiellement la façon dont les banques (et la plupart des bibliothèques de devises) gèrent le stockage des données et vous éviteront des problèmes plus tard.

J'ai appris cela à la dure car ce n'est pas vraiment un sujet courant; cela sauve peut-être quelqu'un d'autre de faire la même chose.

3
Nathan Cox