Django 表单动态生成和验证

Django forms dynamic generation and validation

假设我有以下模型

class Foo(models.Model):
    title = models.CharField(default='untitled')
    # Bar is a MPTT class, so I'm building a tree here
    # Should not matter for my question...
    root = models.ForeignKey(Bar)
    leaf = models.ForeignKey(Bar)

要创建新的 Foo 对象,我想像这样使用 ModelForm:

class FooForm(ModelForm):
    # possibly custom validation functions needed here...
    class Meta:
        model = Foo
        fields = '__all__'

我的观点是这样的:

def create(request, leaf_id=None):
    form = FooForm(data=request.POST or None)

    if form.is_valid():
        new = form.save()
        return redirect('show.html', root_id=new.root.id)

    return render('create_foo.html', 
                  { 'form': form })

如您所见,视图函数应该用于处理两种不同的用例:

  1. /foo/create/
  2. /foo/create/4其中 4 是叶 ID。

如果给出叶 ID,则表单显然不需要为此显示表单域。此外,root 可以从 leaf 确定,因此也不是必需的。

我知道我可以动态更改使用的小部件,因此我可以将它们切换为 HiddenInput,但我什至不希望将它们显示为对用户隐藏。但是如果我动态排除它们,它们将无法用于表单验证,整个过程将在验证过程中失败。

我想要实现的是:仅向用户显示尚未预填的表单字段。是否有适用于这种情况的最佳实践?

您可以通过覆盖 FooForm__init__() 方法来做到这一点。

我们覆盖 __init__() 方法并检查 instance 参数是否传递给了表单。如果通过了 instance,我们将禁用 rootleaf 表单字段,这样它就不会显示在模板中。

当请求的类型为 foo/create/4 时,我们会将 instance 参数传递给表单,即 leaf_id 不是 None.

forms.py

class FooForm(ModelForm):    

    def __init__(self, *args, **kwargs):
        super(FooForm, self).__init__(*args, **kwargs) # call the 'super()' init method
        instance = getattr(self, 'instance', None) # get the `instance` form attribute 
        if instance and instance.id: # check if form has 'instance' attribute set and 'instance' has an id
            self.fields['root'].widget.attrs['disabled'] = 'disabled' # disable the 'root' form field
            self.fields['leaf'].widget.attrs['disabled'] = 'disabled' # disable the 'leaf' form field

    # custom validation functions here
    ....

    class Meta:
        model = Foo
        fields = '__all__'

在我们看来,我们首先检查 leaf_id 参数是否传递给了这个视图。如果传递了 leaf_id,我们将检索 Foo 对象,其叶 ID 为 leaf_id。此 instance 然后在初始化表单时传递,并在调用 form.save() 时更新。我们将使用 instance 将值作为 instance 上设置的属性来填充表单。

如果leaf_id没有通过,那么我们用data参数初始化FooForm

views.py

def create(request, leaf_id=None):
    # Get the instance if any
    instance = None
     if leaf_id:
        instance = Foo.objects.get(leaf_id=leaf_id) # get the 'Foo' instance from leaf_id

    # POST request handling
    if request.method=='POST':    
        if instance:      
            form = FooForm(data=request.POST, instance=instance) # Populate the form with initial data and supply the 'instance' to be used in 'form.save()'
        else:
            form = FooForm(data=request.POST)

        if form.is_valid():
            new = form.save()
            return redirect('show.html', root_id=new.root.id)

        return render('create_foo.html', 
                  { 'form': form })

    # GET request handling    
    if instance:
        form = FooForm(initial=instance._data, instance=instance) # form will be populated with instance data
    else:
        form = FooForm() # blank form is initialized        
    return render('create_foo.html', 
                  { 'form': form })