【问题标题】:Add fields to Django ModelForm that aren't in the model将不在模型中的字段添加到 Django ModelForm
【发布时间】:2011-06-07 15:12:55
【问题描述】:

我有一个看起来像这样的模型:

class MySchedule(models.Model):
  start_datetime=models.DateTimeField()
  name=models.CharField('Name',max_length=75)

随之而来的是它的 ModelForm:

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")

基本上,我试图将模型中的 DateTime 字段分解为 3 个更易于使用的表单字段——一个日期选择器、一个小时下拉列表和一个分钟下拉列表。然后,一旦我得到三个输入,我将它们重新组合成一个 DateTime 并将其保存到模型中。

几个问题:

1) 这完全是错误的做法吗?我不想在模型中创建小时、分钟等字段,因为这基本上只是中间数据,所以我想要一种将 DateTime 字段分解为子字段的方法。

2) 我遇到的困难是当 startdate 字段为空白时——似乎它永远不会被检查为非空白,并且稍后当程序需要一个日期并获取时最终抛出一个 TypeError没有。 Django 在哪里检查空白输入,并引发最终返回表单的错误?这是我的责任吗?如果是这样,我该怎么做,因为它不评估 clean_startdate() 因为 startdate 不在模型中。

3) 有没有更好的方法通过继承来做到这一点?也许继承 BetterScheduleForm 中的 MyScheduleForm 并在那里添加字段?我该怎么做? (我已经玩了一个多小时,似乎无法理解)

谢谢!

[编辑:] 没有返回 self.cleaned_data -- 原来在复制/粘贴中丢失了

【问题讨论】:

  • 一般来说,ModelForm 可以包含你想要的任何字段。在这方面,它就像一个正常的表格。唯一需要担心的是,如果模型中不存在这些字段,您将需要实现初始数据、适当的 clean() 方法和适当的 save() 方法,因为 ModelForm 会尝试使用自动神奇地生成这些东西模型。

标签: python django django-forms


【解决方案1】:

1:我不认为这是错误的,因为你有一些非常具体的事情发生在那里:

  • 具体时间条目(中午,下午 5 点结束..)
  • startminutes 以 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。继承是为了重用代码:)

如果您希望将其用于多个DateTimeFields,是的,您可以使用表单继承。您可以像现在一样定义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

还有一些其他“第 3 方”拆分日期时间小部件也值得在互联网上查看!

【讨论】:

  • required=True 是 django 表单字段的默认设置。
  • 好点,但他不应该遇到#2 的问题。我想知道为什么该字段没有验证?嗯
  • 感谢您的回复——以下是对您问题的一些回复: 1) 我知道默认值是 required=True。问题是,如果非模型字段(开始日期、开始时间等)留空,它不会引发异常并将“缺失字段”信息返回到我的表单。我希望它检查空白并在有空白字段时犹豫,但它似乎错过了这一点,直到空白字段稍后在 clean() 中导致问题 2)我尝试添加一个 clean_startdate() 函数,但它似乎永远不会被叫。但是,我的模型中的字段的 clean_ 函数确实会被调用。
  • SplitDateTimeWidget 完全符合我的需要,感谢您指出
【解决方案2】:
  1. 如果我是你,我会使用 customised Django-admin date/time widget(s) 输入日期/时间条目。

  2. 关于表单验证,请确保传递与请求关联的表单以显示基于表单的错误。 (下面的示例代码)

  3. 至于使用继承,这对这个用例来说有点过头了,因为它没有任何用途,最好保持简单。

示例代码:

if request.POST:
    form = MyScheduleForm(request.POST)
    if form.is_valid():
        # Specific stuff with the variables here
        pass
else:
    form = MyScheduleForm()

【讨论】:

  • 是的,我正在使用类似的结构。问题是非模型字段似乎永远不会被检查以查看它们是否为空白,因此如果它们为空白,它们不会引发异常。模型中的字段会被检查,如果它们丢失,我会收到一个错误传递回我的表单
  • 有比它看到的更多。我可以运行上面的代码,它确实得到了验证!
【解决方案3】:

对于可能包含空白值的表单字段,您必须按如下方式声明该字段:

start_datetime=models.DateTimeField(blank=True, null=True)

这告诉表单它可以是blank,并且数据库字段可以是null。这可能会解决这个问题。

如果您尝试包含不属于模型的字段,为什么要使用 ModelForm? ModelForms 旨在快速创建直接绑定到模型的表单。当然,它们有各种自定义,但在我看来,更改实际字段似乎是常规表单的用途。

否则,如果你只是想拆分formmm的VIEW,而不是表单本身,创建一个自定义的widget来显示DateTime字段,例如SplitDateTimeWidget。对其进行子类化,并为下拉列表的值提供您的 CHOICES。

【讨论】:

  • -1 如果我想使用这个功能的大部分并进行一点点自定义怎么办?
  • @Alvaro 我在回答中提到了这一点。某些自定义有钩子,但添加大量不映射到模型字段的表单字段不是模型表单的用途。
【解决方案4】:

好的,我想我想通了:

从 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')

所以我想这主要回答了我的问题。但是,我还有 1 个问题:

最好在模型 clean() 中检查空白值,还是有更好的方法来做到这一点?检查模型中的空格而不是 ModelForm 中的空白似乎很不自然——表单字段的验证是否应该标记必填字段中缺少的输入?

感谢大家的帮助。

【讨论】:

  • 一定要在ModelForm 上使用clean_field 方法。我很困惑,因为当我复制您的表单时,我确实得到了一个 ValidationError 用于添加到我的 ModelForm 的额外字段。我创建了一个 ModelForm,添加了一个像您一样的干净方法,并触发了验证。您的 ModelForm 是否还有其他内容?
  • 我认为这里的问题(在搞砸之后) - 如果该字段为空白,它永远不会被添加到cleaned_data,并且 clean_field 方法永远不会运行。然后它运行表格 clean() 和模型 clean()。就我而言,模型 clean() 由于我在 clean() 中进行的比较而导致 TypeError 异常,这导致我的代码失败。我认为这里有这个问题(如果我错了,请纠正我)——空白值不会触发异常,它们只会导致 is_valid() 失败,并且会在 error_list 中添加错误。由于我有另一个错误(实际上确实抛出了一个异常),这导致了我的问题
猜你喜欢
  • 2015-08-31
  • 2015-09-06
  • 2012-11-13
  • 2013-05-14
  • 2015-06-25
  • 2018-03-14
  • 2012-01-26
  • 2023-03-09
  • 2018-11-01
相关资源
最近更新 更多