Django - ModelForm动态字段更新

5
我正在尝试更新 ModelForm 中的某些字段,这些字段是不固定的。(我只有视图自动填充的 tutor 字段)
模型:
class Session(models.Model):
  tutor = models.ForeignKey(User)
  start_time = models.DateTimeField()
  end_time = models.DateTimeField()
  status = models.CharField(max_length=1)

表格:
class SessionForm(forms.ModelForm):
  class Meta:
    model = Session
    exclude = ['tutor']

在某些情况下,我只需要更新end_time,有时只需要更新start_timeend_time。在视图中该如何实现?
编辑:
我已经给出了示例,但不限于这些示例,我需要更新的字段并没有预先定义,我需要能够更新任何字段。

当你说“更新”时,你指的是什么?你会渲染所有表单字段还是只渲染需要更新的字段? - okm
我只渲染那些已更新的内容。(更新指更改值) - Pierre de LESPINAY
你能举个例子吗,比如使用场景?我还不太确定你的目的。 - okm
4个回答

10

我以前也遇到过类似的问题,虽然不太好看,但确实很有效。它涉及在运行时动态创建类型,然后使用该类型。更多文档可以参见Django的DynamicModels

接下来是你的要求:

  • 你想通过表单更新模型
  • 你想在运行时选择性地指定要更新哪些字段

因此,以下是一些代码:

def create_form(model, field_names):
    # the inner class is the only useful bit of your ModelForm
    class Meta:
        pass
    setattr(Meta, 'model', model)
    setattr(Meta, 'include', field_names)
    attrs = {'Meta': Meta}

    name = 'DynamicForm'
    baseclasses = (forms.ModelForm,)
    form = type('DynamicForm', baseclasses, attrs)
    return form

def my_awesome_view(request):
    fields = ['start_time', 'end_time']
    form = create_form(Session, fields)
    # work with your form!

我喜欢这个。有什么不好看的吗? - Pierre de LESPINAY
我必须说,这并不直观。当我考虑动态表格的实现时,我写下了“不太好看”的评论。但实际上,这看起来相当优雅。 - Josh Smeaton
为什么不直接生成并返回表单呢?使用type()构建对于具有动态字段的动态模型非常有用,在您的代码中,attrs只有Meta类。无论如何。 - okm
@okm,我有意让这个例子比实际需要的要长,以展示答案中的不同部分。不过,我仍然会使用一个函数来创建动态类,因为在视图中声明元类型会显得可疑。 - Josh Smeaton
如果我需要覆盖某些字段的属性,我会在__init__方法中执行。那么我们如何动态编写__init__方法呢? - Nava
@Navaneethan 我没有尝试过,也不确定,但你可以尝试使用带有自定义init方法的普通ModelForm,并将其用于“baseclasses”元组中,而不是ModelForm。 - Josh Smeaton

1

将表单字段设置为可空,并使用“clean”方法,您可以向字段添加逻辑,例如:

class SessionForm(forms.ModelForm):
    def clean_end_date(self):
        cd = self.cleaned_data
        if (cd["start_date"] and cd["end_date"]) and cd["end_date"] < cd["start_date"]:
            raise forms.ValidationError("WTF?!")
        if not (cd["start_date"] or cd["end_date"]):
            raise forms.ValidationError("need one date")
        return cd['end_date']

如果你想改变值,可以在return语句中使用不同的值。

这是你可能需要进行验证的内容。

如果你愿意,你可以在视图中复制GET字典并在实例化表单之前更新值。

def my_view(request):
    r_data = request.GET.copy()
    r_data.merge(request.POST)

    data = dict([(key, my_function(key, value)) for key, value in r_data.iteritems() if key in MyForm.fields])
    form = MyForm(data=data)
    [...]

希望能有所帮助。


虽然没有完全回答我的问题,但你的验证规则很有趣,我会使用它们 :) - Pierre de LESPINAY

0

根据您的问题,您需要一个要求,该要求使用相同的模型,但在您的ModelForm中隐藏了不同的字段,根据您的需求。

如果我理解您的问题,那么您需要创建两个不同的基于ModelForm类的模型,这两个类都具有Meta类中相同的模型值。排除和包含您需要的字段。因此,现在您有了不同的ModelForm类,可以在视图逻辑中调用它们。

通过这种方式,您将拥有两个依赖于相同模型的不同表单,具有不同的默认字段。

通过巧妙的URL和视图设计,您将能够使用上述想法从同一模型中提供多个表单。


0
您可以像这样调用表单的构造函数:
class SessionForm(forms.ModelForm):
    class Meta:
        model = Session
        exclude = ['tutor', 'start_time']

    def __init__(self, your_extra_args, *args, **kwargs):
        super(SessionForm, self).__init__(*args, **kwargs)
        if need_end_time_start_time(your_extra_args):
            self.fields['start_time'] = forms.DateTimeField()     

要使用这个类,你必须向你的表单传递一个名为"your_extra_args"的参数:
session_form = SessionForm('foo')

如果我有一个有10个字段的模型,每个字段都可以独立更新,你的意思是我需要为每个字段都这样做吗? - Pierre de LESPINAY
在这种情况下,你应该有一个规则来确定哪个需要更新,对吧?给你的10个字段命名一个约定,比如field_1、field_2等,然后你可以使用循环一次性完成。 - vutran
如果你使用kwargs,你可以使用setattr轻松地翻译值。但我认为,在视图中复制你的post/get查询字典并进行修改可能是一个不错的方法,这样你就可以遍历字段并计算你想要的内容,然后通过表单验证你的修改。 - christophe31
@christophe31,我不这么认为,使用构造函数可以避免冗余字段,使您的HTML更小、更快速验证...这是保持逻辑流程和易于理解的好方法。 - vutran

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接