简体   繁体   English

Django 自定义表单字段在渲染和提交中更改值

[英]Django custom form field change value in render and submit

I've implemented a custom form field for converting my model integer field, which represent meter units, to a form float field representing kilometer units.我已经实现了一个自定义表单字段,用于将表示米单位的模型整数字段转换为表示公里单位的表单浮点字段。 ie 3500 meters saved in my model integer field would display 3,5 in form float field, and when the form is sent it need to convert back to integer.即保存在我的模型整数字段中的 3500 米将在表单浮点字段中显示 3,5,并且在发送表单时需要将其转换回整数。 In order to achieve this i divide the value by 1000 before display it and multiply it by 1000 when i save it.为了实现这一点,我在显示之前将值除以 1000,然后在保存时将其乘以 1000。

The render part works fine (either by dividing by 1000 in widget render or rather prepare_value in form field).渲染部分工作正常(在小部件渲染中除以 1000,或者在表单字段中除以 prepare_value)。

The problem comes when the form throws an error and values need to be redisplayed.当表单抛出错误并且需要重新显示值时,问题就出现了。 In this case the form value will be passed to it (which is the float one 3,5) and the value is redivided and is displayed as 0,0035.在这种情况下,表单值将传递给它(浮点数 1 3,5)并且该值被重新划分并显示为 0,0035。 So i don't need to divide the value by 1000 again.所以我不需要再次将值除以 1000。

class KmInput(NumberInput):

    def render(self, name, value, attrs=None):
        try:
            value = str(float(value or 0) / 1000)
        except ValueError:
            pass
        return super(KmInput, self).render(name, value, attrs)

class MeterToKmField(forms.FloatField):
    widget = KmInput

    def __init__(self, max_value=None, *args, **kwargs):
        super(MeterToKmField, self).__init__(max_value, 0, *args, **kwargs)

    def to_python(self, value):
        result = super(MeterToKmField, self).to_python(value)
        value *= 1000
        return result

class DistanceForm(forms.ModelForm):
    distance = MeterToKmField(help_text="km")

    class Meta:
        model = Distance

Am i missing something?我错过了什么吗?

UPDATE:更新:

As Peter DeGlopper suggested, i've implemented _format_value in my custom widget, but i still get this method called when the form raise an error, making the value that is already divided by 1000, gets divided again.. Here's what i did:正如Peter DeGlopper建议的那样,我已经在我的自定义小部件中实现了 _format_value,但是当表单引发错误时,我仍然会调用此方法,使已经除以 1000 的值再次被除..这就是我所做的:

class KmInput(NumberInput):

    def _format_value(self, value):
        try:
            return str(float(value or 0) / 1000)
        except ValueError:
            return value

class MeterToKmField(forms.FloatField):
    widget = KmInput

    def __init__(self, max_value=None, *args, **kwargs):
        super(MeterToKmField, self).__init__(max_value, 0, *args, **kwargs)

    def to_python(self, value):
        result = super(MeterToKmField, self).to_python(value)
        result *= 1000
        return result

I had a similar problem displaying price fields that are internally stored as integers.我在显示内部存储为整数的价格字段时遇到了类似的问题。 This is what I learned:这是我学到的:

You could overwrite the bounddata method in your field to return the initial rather than the entered data.您可以覆盖字段中的 bounddata 方法以返回初始数据而不是输入的数据。 The result of bound data still passes through prepare_value later, so you could implement this the same way you did before:绑定数据的结果稍后仍会通过 prepare_value,因此您可以像之前一样实现它:

class MeterToKmField(forms.FloatField):
    def bound_data(self, data, initial):
        return initial

    def prepare_value(self,value):
        #...same as before, just divide by 1000

However this might not be what you want, as changes made by the user might be lost.但是,这可能不是您想要的,因为用户所做的更改可能会丢失。 My preferred solution therefore is based on the fact that the bound value in prepare_value is unicode and the unbound one is an integer.因此,我的首选解决方案是基于 prepare_value 中的绑定值是 unicode 而未绑定值是整数这一事实。 So you could do something like:所以你可以这样做:

class MeterToKmField(forms.FloatField):
    def prepare_value(self, value):
        #python 2.x ,use str instead of basestring for python 3.x
        if isinstance(value, basestring):
            #in case of bound data, is already divided by 100
            return value
        else:
            #raw data
            return float(value)/ 1000

    #don't overwrite bound_data in this case

From looking at how date fields are implemented, it looks like the best way to do this is with both a custom field (so you can do the to_python customization) and a custom widget - one that implements _format_value .从查看日期字段的实现方式来看,似乎最好的方法是使用自定义字段(因此您可以进行to_python自定义)和自定义小部件 - 一个实现_format_value小部件。

So something like:所以像:

class KmInput(NumberInput):
    def _format_value(self, value):
        return str(value / 1000)

And then the form as above.然后是上面的表格。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM