要自定义字段的呈现方式,您还需要定义一个小部件。
以下代码显示了 django 中小部件的一些职责:
- 渲染表单标签(超类 TextInput 负责这个)
- 格式化在表单域内显示的值。
这里有一个陷阱:值的类可能会有所不同,因为您可能会呈现来自字段的已清理值,或者来自表单 POST 的未清理数据。因此测试我们是否在 _format_value 中有一个数值;如果是字符串,则保持原样。
- 与初始数据相比,判断值是否发生了变化。当您使用具有默认值的表单集时非常重要。
请注意,小部件的代码灵感来自 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,这是由于使用浮点数引起的精度损失。如果您不想丢失某些精度,您可能需要更改数据类型。