2017-10-19 4 views
1

J'ai un champ modèle full_time_equivalent:Pourquoi la validation de Django decimal max_digits échoue-t-elle avec un comportement étrange?

full_time_equivalent = models.DecimalField(
    max_digits=5, 
    decimal_places=2, 
    default=100, 
    validators=[ 
     MinValueValidator(Decimal(0)), 
     MaxValueValidator(Decimal(100)) 
    ] 
) 

Pour vous assurer que les validateurs je feu ai override save avec:

def save(self, *args, **kwargs): 
    # Run validations 
    self.full_clean() 
    return super().save(*args, **kwargs) 

Avec le test suivant:

project2_membership = ProjectMembership(
     user=self.new_user, 
     project=project2, 
     is_project_manager=False, 
     full_time_equivalent=10.01 
    ) 

Lorsque je l'étape dans la validation, la valeur suivante est affichée et l'erreur correspondante:

Decimal('10.0099999999999997868371792719699442386627197265625') 


django.core.exceptions.ValidationError: 
{'full_time_equivalent': ['Ensure that there are no more than 5 digits in total.'] 

Qu'est-ce que je fais mal?

+0

avait un regard sur le [django-code] (https://github.com/django/django/blob/399a8db33b14a1f707912ac48a185fb0a1204913/django/db/models/base.py) ... ce qui se passe si vous appelle seulement 'self.clean()'? Je veux juste savoir d'où vient ce problème. – Thomas

+0

Ressemble à un problème de virgule flottante. Si c'est le cas, alors c'est un bug dans le validateur django (mais il utilise le type '' Decimal'' qui ne devrait pas avoir de tels problèmes) ou dans votre classe obtenant l'argument '' 10.01''. – allo

+0

Je suppose que vous devrez peut-être ajouter quelques arrondis. Dans le shell python, quand je tape 'print (Decimal (10.01))', j'obtiens '10.009999 ...' comme vous le faites. – jcfollower

Répondre

1

La valeur décimale 10.01 ne peut pas être exprimée exactement comme un flottant. Lorsque la valeur est convertie en un nombre décimal, vous finissez avec Decimal('10.0099999999999997868371792719699442386627197265625'), ce qui est très proche de Decimal('10.01'), mais échoue votre validation max_digits.

Vous pouvez empêcher l'erreur en utilisant la chaîne '10.01' ou la décimale Decimal('10.01') dans votre test.

from decimal import Decimal 

project2_membership = ProjectMembership(
    user=self.new_user, 
    project=project2, 
    is_project_manager=False, 
    full_time_equivalent=Decimal('10.01') 
)