Skip to main content
 首页 » 编程设计

python之Django 科学记数法输入验证

2025年01月19日34soundcode

我的模型上有以下字段:

class Range(models.Model): 
    optimal_low = models.DecimalField(max_digits=30, decimal_places=8) 
    optimal_high = models.DecimalField(max_digits=30, decimal_places=8) 

下面是我如何将它们放入表单中(因为表单的主要对象不是这个模型,我只需要字段,不想重复 max_digits 和 decimal_places。

class ReadingMappingForm(forms.ModelForm): 
    optimal_low = Range._meta.get_field('optimal_low').formfield() 
    optimal_high = Range._meta.get_field('optimal_high').formfield() 

似乎 django 允许开箱即用地以科学记数法输入小数,但在超过某个阈值时会出现故障。

在表单中,如果我输入 1.5E9 它工作正常,并且该值被保存为 1500000000.00000000(这是一个 online scientific notation calculator )。

但是,如果我输入 1.5E10,它会显示:

Ensure that there are no more than 8 decimal places.

这是错误的,因为我没有添加任何小数位。事实上,如果我以正常的表示法输入 1.5E10,即使添加了 8 位小数,即 15000000000.00000000 它也能正常工作。

所以我认为某些东西在引擎盖下无法正常工作...

编辑

我在控制台测试了这个字段,它在那里出错了:

from django.forms import DecimalField 
>>> f = DecimalField(max_digits=30, decimal_places=8) 
>>> f.clean('1.5E9') 
Decimal('1.5E+9') 
>>> f.clean('1.5E10') 
Traceback (most recent call last): 
  File "myproject/env/lib/python3.5/site-packages/django/core/management/commands/shell.py", line 69, in handle 
    self.run_shell(shell=options['interface']) 
  File "myproject/env/lib/python3.5/site-packages/django/core/management/commands/shell.py", line 61, in run_shell 
    raise ImportError 
ImportError 
 
During handling of the above exception, another exception occurred: 
 
Traceback (most recent call last): 
  File "<console>", line 1, in <module> 
  File "myproject/env/lib/python3.5/site-packages/django/forms/fields.py", line 168, in clean 
    self.run_validators(value) 
  File "myproject/env/lib/python3.5/site-packages/django/forms/fields.py", line 157, in run_validators 
    raise ValidationError(errors) 
django.core.exceptions.ValidationError: ['Ensure that there are no more than 8 decimal places.'] 

请您参考如下方法:

它似乎确实是 DecimalValidator 中的错误,解释如下

您声明的数字 1.5E10 被解析为具有属性 _int '15' 和 _exp 9 的对象

在 DecimalValidator __call__ 方法中,小数位数计算为

decimals = abs(exponent) 

这比进一步触发以下

if self.decimal_places is not None and decimals > self.decimal_places: 
    raise ValidationError( 
        self.messages['max_decimal_places'], 
        code='max_decimal_places', 
        params={'max': self.decimal_places}, 
    ) 

似乎下面的修复是这样的

if exponent < 0: 
     decimal = abs(exponent) 
else: 
     decimal = 0 

2.0 版的修复如下所示

# If the absolute value of the negative exponent is larger than the 
# number of digits, then it's the same as the number of digits, 
# because it'll consume all of the digits in digit_tuple and then 
# add abs(exponent) - len(digit_tuple) leading zeros after the 
# decimal point. 
if abs(exponent) > len(digit_tuple): 
    digits = decimals = abs(exponent) 
else: 
    digits = len(digit_tuple) 
    decimals = abs(exponent)