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 })
如您所见,视图函数应该用于处理两种不同的用例:
/foo/create/
/foo/create/4
其中 4
是叶 ID。
如果给出叶 ID,则表单显然不需要为此显示表单域。此外,root
可以从 leaf
确定,因此也不是必需的。
我知道我可以动态更改使用的小部件,因此我可以将它们切换为 HiddenInput
,但我什至不希望将它们显示为对用户隐藏。但是如果我动态排除它们,它们将无法用于表单验证,整个过程将在验证过程中失败。
我想要实现的是:仅向用户显示尚未预填的表单字段。是否有适用于这种情况的最佳实践?
您可以通过覆盖 FooForm
的 __init__()
方法来做到这一点。
我们覆盖 __init__()
方法并检查 instance
参数是否传递给了表单。如果通过了 instance
,我们将禁用 root
和 leaf
表单字段,这样它就不会显示在模板中。
当请求的类型为 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 })
假设我有以下模型
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 })
如您所见,视图函数应该用于处理两种不同的用例:
/foo/create/
/foo/create/4
其中4
是叶 ID。
如果给出叶 ID,则表单显然不需要为此显示表单域。此外,root
可以从 leaf
确定,因此也不是必需的。
我知道我可以动态更改使用的小部件,因此我可以将它们切换为 HiddenInput
,但我什至不希望将它们显示为对用户隐藏。但是如果我动态排除它们,它们将无法用于表单验证,整个过程将在验证过程中失败。
我想要实现的是:仅向用户显示尚未预填的表单字段。是否有适用于这种情况的最佳实践?
您可以通过覆盖 FooForm
的 __init__()
方法来做到这一点。
我们覆盖 __init__()
方法并检查 instance
参数是否传递给了表单。如果通过了 instance
,我们将禁用 root
和 leaf
表单字段,这样它就不会显示在模板中。
当请求的类型为 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 })