[英]Django percentage field
我正在尝试在 Django 中创建一个百分比字段,用户只需填写 40 即可获得 40%。 输入框右侧会有一个百分号,让他们知道应该填写一个百分比。 0.4 必须存储在数据库中。 到目前为止,我已经尝试了以下方法:
class PercentageField(fields.FloatField):
widget = fields.TextInput(attrs={"class": "percentInput"})
def to_python(self, value):
val = super(PercentageField, self).to_python(value)
if is_number(val):
return val/100
return val
def prepare_value(self, value):
val = super(PercentageField, self).prepare_value(value)
if is_number(val):
return str((float(val)*100))
return val
def is_number(s):
if s is None:
return False
try:
float(s)
return True
except ValueError:
return False
它可以工作,但问题是,当我发布无效数据并再次呈现表单时,它会将 40 显示为 4000。换句话说,它将数字再次乘以 100 而不会除以它。
有什么建议可以解决吗?
我已经尝试过这个解决方案,但它会重复该值 100 次。 我改正后也有同样的问题。
我正在使用 Python3.5
我找到了解决方案。 我必须检查传入的值是否是字符串。 如果是,我不会乘以 100,因为它来自表单。 见下文:
class PercentageField(fields.FloatField):
widget = fields.TextInput(attrs={"class": "percentInput"})
def to_python(self, value):
val = super(PercentageField, self).to_python(value)
if is_number(val):
return val/100
return val
def prepare_value(self, value):
val = super(PercentageField, self).prepare_value(value)
if is_number(val) and not isinstance(val, str):
return str((float(val)*100))
return val
从文档中, to_python()
应该在您处理复杂数据类型的情况下使用,以帮助您与数据库进行交互。 我认为更准确的方法是覆盖pre_save()
Field
方法。 从文档:
预保存(模型实例,添加)
在 get_db_prep_save() 之前调用的方法以在保存之前准备值(例如对于 DateField.auto_now)。
最后,它看起来像这样:
def validate_ratio(value):
try:
if not (0 <= value <= 100):
raise ValidationError(
f'{value} must be between 0 and 100', params={'value': value}
)
except TypeError:
raise ValidationError(
f'{value} must be a number', params={'value': value}
)
class RatioField(FloatField):
description = 'A ratio field to represent a percentage value as a float'
def __init__(self, *args, **kwargs):
kwargs['validators'] = [validate_ratio]
super().__init__(*args, **kwargs)
def pre_save(self, model_instance, add):
value = getattr(model_instance, self.attname)
if value > 1:
value /= 100
setattr(model_instance, self.attname, value)
return value
我的情况有点不同,我想要一个比率而不是一个百分比,所以我只允许 0 到 100 之间的值,这就是为什么我需要一个验证器,但这个想法就在这里。
此任务有一个简单的替代方法。 您可以为此使用MaxValueValidator
和MinValueValidator
。
以下是您可以这样做的方法:
from django.db import models
from django.core.validators import MinValueValidator, MaxValueValidator
PERCENTAGE_VALIDATOR = [MinValueValidator(0), MaxValueValidator(100)]
class RatingModel(models.Model):
...
rate_field = models.DecimalField(max_digits=3, decimal_places=0, default=Decimal(0), validators=PERCENTAGE_VALIDATOR)
基于@elachere 答案和Django 文档,这是我使用的代码:
from decimal import Decimal
from django import forms
from django.db import models
class PercentageField(models.DecimalField):
def from_db_value(self, value, expression, connection):
if value is None:
return value
return Decimal(value) * Decimal("100")
def pre_save(self, model_instance, add):
value = super().pre_save(model_instance, add)
setattr(model_instance, self.attname, Decimal(value) * Decimal(".01"))
return Decimal(value) * Decimal(".01")
我遇到了类似的问题,但我希望我的PercentageField
基于DecimalField
而不是FloatField
,这符合有关货币的建议。 在这种情况下,当前接受的答案对我来说不适用于 Django 4.0,原因有两个:
to_python
被调用两次,一次是通过表单的clean
方法(如 文档中所述),另一次是通过get_db_prep_save
( 在文档中提到)。 事实上,事实证明(在Django 源代码中) DecimalField
和FloatField
在这一点上有所不同。prepare_value
根本不会运行。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.