在Django ModelForm中添加不在模型中的字段
我有一个模型,长得像这样:
class MySchedule(models.Model):
start_datetime=models.DateTimeField()
name=models.CharField('Name',max_length=75)
它还有一个对应的表单模型:
class MyScheduleForm(forms.ModelForm):
startdate=forms.DateField()
starthour=forms.ChoiceField(choices=((6,"6am"),(7,"7am"),(8,"8am"),(9,"9am"),(10,"10am"),(11,"11am"),
(12,"noon"),(13,"1pm"),(14,"2pm"),(15,"3pm"),(16,"4pm"),(17,"5pm"),
(18,"6pm"
startminute=forms.ChoiceField(choices=((0,":00"),(15,":15"),(30,":30"),(45,":45")))),(19,"7pm"),(20,"8pm"),(21,"9pm"),(22,"10pm"),(23,"11pm")))
class Meta:
model=MySchedule
def clean(self):
starttime=time(int(self.cleaned_data.get('starthour')),int(self.cleaned_data.get('startminute')))
return self.cleaned_data
try:
self.instance.start_datetime=datetime.combine(self.cleaned_data.get("startdate"),starttime)
except TypeError:
raise forms.ValidationError("There's a problem with your start or end date")
简单来说,我想把模型里的日期时间字段拆分成三个更容易使用的表单字段——一个日期选择器,一个小时下拉框,还有一个分钟下拉框。然后,在我得到这三个输入后,我会把它们重新组合成一个日期时间格式,并保存到模型里。
我有几个问题:
1) 这样做是不是完全错误的方向?我不想在模型里为小时、分钟等创建字段,因为这些基本上只是中间数据,所以我想找个办法把日期时间字段拆分成子字段。
2) 我遇到的困难是,当开始日期字段是空的时候——似乎它从来没有被检查是否为空,结果在程序期待一个日期的时候却得到了None,导致抛出一个类型错误。Django在哪里检查空输入,并抛出最终回到表单的错误?这是我的责任吗?如果是的话,我该怎么做,因为它并不会评估clean_startdate(),因为开始日期不在模型里。
3) 有没有更好的办法用继承来实现这个?也许可以在BetterScheduleForm中继承MyScheduleForm,然后在那儿添加字段?我该怎么做?(我已经试了一个多小时,还是搞不定)
谢谢!
[编辑:] 忘记了返回self.cleaned_data——最开始复制粘贴的时候丢掉了
4 个回答
1: 我觉得这没什么错,因为你这里有一些非常具体的内容:
- 特定的时间输入(中午,结束于下午5点...)
- 开始时间是15分钟为一个单位
2: 更新:下面的评论提到你的字段默认应该是 required=True
。确实如此,如果这个字段留空,你的表单应该会出现 ValidationError
错误。
你能把你提到的 TypeError
发出来吗?这个错误是在 clean()
这个块之外发生的吗?因为如果你在清理函数中没有像你示例那样返回 cleaned_data
,那么即使最初没有抛出任何 ValidationErrors
,你的表单也没有数据可以使用。
无论如何,你可以查看 clean_
方法来进行每个字段的验证。
def clean_startdate(self):
if not self.cleaned_data['startdate']:
raise forms.ValidationError("Must enter a start date")
http://docs.djangoproject.com/en/dev/topics/forms/modelforms/#overriding-the-clean-method
3: 你能在这里澄清一下你想用继承做什么吗?看起来你的字段定义非常特定于这个表单,所以它就应该放在 MyScheduleForm
里。继承是为了重用代码 : )
如果你想把这个用于多个 DateTimeField
,是的,你可以使用表单继承。你可以像现在这样定义一个 ModelForm
,然后继承它,并重写父类的 Meta
,就像文档中展示的那样,这样可以在多个模型上使用:
http://docs.djangoproject.com/en/dev/topics/forms/modelforms/#form-inheritance
我还建议你看看 Django 是如何实现它的 SplitDateTimeWidget 的(查看源代码): http://docs.djangoproject.com/en/dev/ref/forms/widgets/#django.forms.SplitDateTimeWidget
网上还有一些其他的“第三方”分割日期时间小部件,值得一看!
好的,我想我明白了:
从 Django 1.2 开始,运行 is_valid() 会在 ModelForms 上触发模型的验证。我原本以为在调用模型的 clean() 函数之前,字段会先检查是否为空,所以我的 clean 函数没有检查空值或 None 类型。基本上,我模型中的 clean() 函数看起来像这样:
def clean(self):
if self.start_datetime > datetime.now():
raise ValidationError('Start date can\'t be in the future')
所以我想这基本上回答了我的问题。不过,我还有一个问题:
在模型的 clean() 函数中检查空值是最好的做法吗,还是有更好的方法?在模型中检查空值感觉有点不太妥当,难道不是应该在 ModelForm 中进行验证,以标记必填字段的缺失输入吗?
感谢大家的帮助。
如果我是你,我会使用定制的Django-admin日期/时间控件来输入日期和时间。
关于表单验证,确保你传递与请求相关的表单,这样才能显示基于表单的错误信息。(下面有示例代码)
至于使用继承,这种情况用上继承就有点多余了,因为它没有什么实际意义,保持简单更好。
示例代码:
if request.POST:
form = MyScheduleForm(request.POST)
if form.is_valid():
# Specific stuff with the variables here
pass
else:
form = MyScheduleForm()