簡體   English   中英

Django 百分比字段

[英]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 之間的值,這就是為什么我需要一個驗證器,但這個想法就在這里。

此任務有一個簡單的替代方法。 您可以為此使用MaxValueValidatorMinValueValidator

以下是您可以這樣做的方法:

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 源代碼中) DecimalFieldFloatField在這一點上有所不同。
  • 對於基於現有實例(例如,用戶可能願意編輯)的表單, prepare_value根本不會運行。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM