在调用 save() 之前,根据 max_digits 和 decimal_places 属性自动舍入 Django 的 DecimalField



我想根据max_digits和decimal_places属性自动绕Django的十分位域围绕Decimalfield,然后在modelform中调用Save Save()函数。

>

当前使用以下内容:

  • django 1.8
  • Python 2.7

我到目前为止尝试过的。

https://djangosnippets.org/snippets/10554/


models.py

amount = models.DecimalField(max_digits = 19, decimal_places = 2)

views.py

P.S。将其应用于不同的领域和不同的模型

data = {"amount" : 100.1234,"name":"John Doe",...}
form = My_form(data)
if form.is_valid(): //the error throws from here.
    form.save()
else:
    raise ValueError(form.errors)

forms.py

我计划在清洁()函数中清洁字段,并在所有小数区域中进行舍入,但是当我尝试打印RAW_DATA时,没有"金额"字段。

class My_form(forms.ModelForm):
    Class Meta:
        model = My_model
        fields = ('amount','name')
    def clean(self):
        raw_data = self.cleaned_data
        print(raw_data) //only prints {'name' : 'John Doe'}

您主要遇到错误,因为forms.DecimalField具有 models.DecimalField的单独验证器:

data = {'amount': 1.12345 }
class NormalForm(forms.Form):
    amount = forms.DecimalField(max_digits = 19, decimal_places = 2)
normal_form = NormalForm(data)
normal_form.is_valid()  # returns False
normal_form.cleaned_data  # returns {}

forms.DecimalField默认用于具有models.DecimalField类字段的模型的表单。您可以做这样的事情:

from django import forms
from django.db import models
from decimal import Decimal
def round_decimal(value, places):
    if value is not None:
        # see https://docs.python.org/2/library/decimal.html#decimal.Decimal.quantize for options
        return value.quantize(Decimal(10) ** -places)
    return value
class RoundingDecimalFormField(forms.DecimalField):
    def to_python(self, value):
        value = super(RoundingDecimalFormField, self).to_python(value)
        return round_decimal(value, self.decimal_places)
class RoundingDecimalModelField(models.DecimalField):
    def to_python(self, value):
        # you could actually skip implementing this
        value = super(RoundingDecimalModelField, self).to_python(value)
        return round_decimal(value, self.decimal_places)
    def formfield(self, **kwargs):
        defaults = { 'form_class': RoundingDecimalFormField }
        defaults.update(kwargs)
        return super(RoundingDecimalModelField, self).formfield(**kwargs)

现在您使用models.DecimalField的任何地方,而是使用RoundingDecimalModelField。您与这些型号一起使用的任何表单现在也将使用自定义表单字段。

class RoundingForm(forms.Form):
    amount = RoundingDecimalFormField(max_digits = 19, decimal_places = 2)
data = {'amount': 1.12345 }
rounding_form = RoundingForm(data)
rounding_form.is_valid()  # returns True
rounding_form.cleaned_data  # returns {'amount': Decimal('1.12')}

如果您直接分配给模型实例,则无需担心它。字段对象将将值(将其舍入)量化为您在模型定义中设置的小数点级别。

如果您要处理ModelForm,则默认的DecimalField将要求任何输入匹配模型字段的小数点。一般而言,处理此问题的最简单方法可能是将模型DecimalField分类,删除小数点特定的验证器并依靠基础转换来量化数据:

from django.db.models.fields import DecimalField
class RoundingDecimalField(DecimalField):
    @cached_property
    def validators(self):
        return super(DecimalField, self).validators
    def formfield(self, **kwargs):
        defaults = {
            'max_digits': self.max_digits,
            'decimal_places': 4, # or whatever number of decimal places you want your form to accept, make it a param if you like
            'form_class': forms.DecimalField,
        }
        defaults.update(kwargs)
        return super(RoundingDecimalField, self).formfield(**defaults)

然后在您的模型中:

amount = RoundingDecimalField(max_digits = 19, decimal_places = 2)

(实际上并没有将字段类放在与模型同一字段中,例如。)

这在绝对方面可能不太正确,而不是定义自定义字段表格,这是我的第一个建议,但使用的工作较少。

如果要设置窗口小部件以限制小数号输入表单呈现,则只需使用此代码

from django import forms
class DecimalNumberInput(forms.NumberInput):
    def get_context(self, name, value, attrs):
        context = super().get_context(name, value, attrs)
        try:
            if self.attrs['decimal_places'] and isinstance(self.attrs['decimal_places'], int) :
                context['widget']['value'] = str(round(float(context['widget']['value']),self.attrs['decimal_places']))
        except Exception as e:
            pass
        return context
class NormalForm(forms.Form):
    amount = forms.DecimalField(max_digits = 19, decimal_places = 2 , widget=DecimalNumberInput(attrs={'decimal_places':2}))

最新更新