Django:在表单中嵌套表单(递归...)
我很喜欢Django的表单库,但如果一个表单可以包含其他表单,那就更好了。
我心中的理想是这样的:
- 我有一个表单,它的表现和普通表单一样,比如叫做
SuperForm
的类。 SuperForm
可以包含几个普通表单,甚至可以包含(递归的)SuperForms
。- 你可以把它和数据绑定(让它变成绑定状态),调用
is_valid()
等等……
这样在Django里或者通过外部应用实现这个功能可能吗?
更新
我发现很多人没有理解我想要的东西。是我的错,我没有给出具体的使用场景。
使用场景:一个页面应该允许用户更新他们的电子邮件和电话号码。电子邮件来自 django.contrib.auth
,而电话号码来自我们自定义的模型。
这两个输入应该放在一个单独的 <form>
标签里。因为 ModelForm
使用起来很简单,所以我不想手动自己创建一个表单。
我想要一个容器,里面包含 django.contrib.auth.models.User
的 ModelForm
和我们自定义模型的 ModelForm
。
我不想自己去遍历这两个表单(在其他使用场景中可能会有更多的表单),检查它们是否有效。
如果我在这个容器上调用 is_valid()
或 save()
,就会自动调用表单对应的方法。
4 个回答
我觉得你对表单的理解可能有些偏差。
表单其实就是用来输入数据的。至于这些数据怎么处理,那就看你自己了。
当然,你可以在表单上运行 is_valid,这样可以检查你指定的模型表单。更棒的是,你可以在同一个表单上检查多个模型表单。模型表单需要的数据会被处理,而多余的数据会被忽略。如果 is_valid 检查失败,你仍然可以像往常一样把数据返回去,让用户进行修改。
乍一看,你的想法似乎有点复杂。首先要问的是:“为什么需要这样的功能?”99%的任务可以用Django自带的工具来解决,剩下的1%就只能硬编码了。另外,我还可以推测,提出这样想法背后的问题也很复杂,普通用户可能很难理解。
我不太确定你想要实现什么,但我曾经遇到过关于动态表单的问题。
如果你有一个表单(或者叫ModelForm
,或者其他你需要的东西),你可以在你的视图中根据需要初始化这个表单多次。
比如说,如果你有一个叫MyForm
的表单,你可以在视图中这样做:
... ...
form1 = MyForm(...)
form2 = MyForm(...)
form3 = MyForm(...)
... ...
然后你可以把所有这些表单发送到模板中。
这些表单的数量不需要是固定的,你可以把多个“动态”的表单放到一个列表里,然后把这个表单列表发送到模板中,按需管理它们。
如果你能多解释一下你的情况,以及你为什么想要实现这个功能,会对你和帮助你的人更有帮助,因为我的回答可能对你没用。
无论如何,我强烈建议你查看一下Django FormSets,因为这是在一个模板中生成和管理动态表单的最佳方式。
你在什么情况下需要这种“动态”表单?
你想获取什么样的信息?
你是在尝试动态创建对象吗?
你是想根据某些信息来决定表单的数量吗?
你打算如何在你的模板或视图中管理这些“动态”表单?
你可以试试这个叫做 django-composite-form 的工具。使用它,你可以写出像下面这样的代码:
这是从这个工具包里附带的示例中摘录的
from django import forms
from django.contrib.auth.forms import UserCreationForm
from composite_form.forms import CompositeForm
from example.models import Profile
class BaseProfileForm(forms.ModelForm):
class Meta:
model = Profile
exclude = ("user")
class ProfileForm(CompositeForm):
form_list = [UserCreationForm, BaseProfileForm]
def clean_address(self):
return "blah"
def save(self, commit=True):
if not self.is_valid():
raise ValueError("Invalid form")
user_form = self.get_form(UserCreationForm)
user = user_form.save()
profile_form = self.get_form(BaseProfileForm)
profile_form.instance.user = user
return profile_form.save()