自定义表单如何将字段输出到django中的窗口小部件

时间:2009-08-27 07:02:43

标签: django django-forms

我有一个名为HourField的自定义字段,可让您输入“1:30”代表一个半小时。对于模型中的FloatField,clean()方法将其转换为1.5。一切正常。现在我遇到了一个问题,当我想创建一个formset时,该字段被渲染为“1.5”而不是“1:30”,这就是我想要的。

是否有某种类似于clean()的方法,但反过来呢?

编辑:使用其他类型的存储方法是不可能的。我有一些其他自定义字段存储为一种类型的对象,但由用户输入为字符串。我使用上面的例子,因为它是最直接的。

4 个答案:

答案 0 :(得分:3)

要自定义字段呈现的方式,您还需要定义窗口小部件。

以下代码显示了django中小部件的一些职责:

  • 渲染表单标记(超类TextInput负责处理)
  • 格式化表单字段内显示的值。 这里有一个问题:值的类可能会有所不同,因为您可能会渲染来自字段的清理值,或来自表单POST的未清除数据。因此,如果我们在_format_value中有一个数值,那么测试;如果它是一个字符串,只需保持原样。
  • 与初始数据相比,判断该值是否已更改。使用具有默认值的formset非常重要。

请注意,小部件的代码的灵感来自django源代码中的widgets.TimeInput,这有助于保持一致。

from django import forms
from django.forms import widgets
from django.forms.util import ValidationError

class HourWidget(widgets.TextInput):

    def _format_value(self, value):
        if isinstance(value, float) or isinstance(value, int):
            import math

            hours = math.floor(value)
            minutes = (value - hours) * 60
            value = "%d:%02d" % (hours, minutes)

        return value

    def render(self, name, value, attrs=None):
        value = self._format_value(value)
        return super(HourWidget, self).render(name, value, attrs)

    def _has_changed(self, initial, data):
        return super(HourWidget, self)._has_changed(self._format_value(initial), data)

class HourField(forms.Field):
    widget = HourWidget

    def clean(self, value):
        super(HourField, self).clean(value)

        import re
        match = re.match("^([0-9]{1,2}):([0-9]{2})$", value)
        if not match:
            raise ValidationError("Please enter a valid hour ( ex: 12:34 )")

        groups = match.groups()
        hour = float(groups[0])
        minutes = float(groups[1])

        if minutes >= 60:
            raise ValidationError("Invalid value for minutes")

        return hour + minutes/60

请注意1:20变为1:19,这是由于使用浮子造成的精度损失。如果您不想丢失一些精度,可能需要更改数据类型。

答案 1 :(得分:1)

如何在表单字段中显示数据是窗口小部件的责任。看起来您需要定义一个自定义窗口小部件,该窗口小部件实现一个render()方法,该方法接受十进制字段值并根据需要输出它。

不幸的是,似乎没有关于创建自定义窗口小部件的任何文档,但您可以查看django.forms.extras.widgets中的代码以获取如何执行此操作的示例。

答案 2 :(得分:1)

from django.forms.widgets import TextInput

class TimeInput(TextInput):
     def _format_value(self, value):
         """Convert float to time"""
         if not value:
             return ''
         fraction, integer = math.modf(value)
         minutes = 0 if fraction == 0 else .6 / fraction
         return "%d:%02d" % (int(integer), int(minutes))

     def render(self, name, value, attrs=None):
        value = self._format_value(value)
        return super(TimeInput, self).render(name, value, attrs)

答案 3 :(得分:0)

我认为您可能想要试用date template tag或者可能想要创建自己的模板标记。这是创可贴 - 但真正的问题是你应该使用datetime模块将时间存储在Python中。这样可以保证格式化。