Django ModelForm - 模板中多个表单,不同的动作视图和重定向
整体目标是:能够在同一个网页上更新和创建多个对象的记录。当用户更新或创建记录后,他们会被引导回到同一个页面。回到这个页面时,他们的操作结果应该能显示出来,并且他们可以安全地刷新页面,而不会重新提交表单。
这是我的 views.py 文件
@login_required()
def index(request):
activity_model_form = ActivityModelForm()
rhi_model_form = RhiModelForm()
activity_list = Activity.objects.all()
rhi_list = Rhi.objects.all()
if request.method == 'POST':
context = {
'activity_list': activity_list,
'rhi_list': rhi_list,
}
return render(request, 'ec/index.html', context)
else:
context = {
'activity_list': activity_list,
'rhi_list': rhi_list,
'activity_model_form': activity_model_form,
'rhi_model_form': rhi_model_form,
}
return render(request, 'ec/index.html', context)
def add_activity(request):
if request.method == 'POST':
aForm = ActivityModelForm(request.POST)
if aForm.is_valid():
aForm.save()
return HttpResponseRedirect(reverse('ec:index'))
def add_rhi(request):
if request.method == 'POST':
rForm = RhiModelForm(request.POST)
if rForm.is_valid():
newRhi = rForm.save()
return HttpResponseRedirect('ec:index')
forms.py 文件
class RhiModelForm(forms.ModelForm):
class Meta:
model = Rhi
class ActivityModelForm(forms.ModelForm):
class Meta:
model = Activity
模板 (index.html)
{% for activity in activity_list %}
{{activity}}
{% endfor %}
<br>
{% for rhi in rhi_list %}
{{rhi}}
{% endfor %}
<br>
<form aciton="{% url 'ec:add_rhi' %}" method="post">
{% csrf_token %}
{{ rhi_model_form }}
<input type="submit" value="Add"/>
</form>
<br>
<form action="{% url 'ec:add_activity' %}" method="post">
{% csrf_token %}
{{ activity_model_form }}
<input type="submit" value="Add" />
</form>
urls.py 文件
urlpatterns = patterns('',
url(r'^$', views.index, name='index'),
url(r'^forms/add_activity',views.add_activity, name='add_activity'),
url(r'^forms/add_rhi',views.add_rhi,name='add_rhi'),
)
index.html 页面是主要页面,应该包含多个表单。在这种情况下,我决定使用模型表单,因为它们提供了很多内置功能。在这里看到的其他类似帖子中,创建不同的视图来处理这些表单的处理似乎是更好的方法。从那里,我想重新加载 index.html,但我想重新查询数据库,以获取更新或新创建的记录。
有两个问题:
1) 模型视图没有保存。当我尝试提交 rhi_model_form 时,我被重定向到 index.html,但没有保存记录。根据我在这里发布的内容,我使用了 newRhi = rForm.save(),但我也尝试过仅仅使用 rForm.save(),结果没有任何效果。为什么有些例子中保存方法会设置为一个变量?这样做有什么好处?无论如何,目前在我这里这两种方法都不管用。
2) 提交后,index 视图被渲染,但它会执行代码中的 if request.method == 'POST' 部分。为什么会这样?如果我刷新页面,它又想重新提交表单。我该如何让这个页面在提交后加载所有想要的数据,并确保刷新不会导致重新提交?
1 个回答
1
好的,我找到了如何重构这段代码的方法。对于其他新手来说,如果你想让用户在同一页面上提交不同的表单,并且不想在刷新页面时遇到麻烦的重新提交问题,下面是一个大致的结构可以参考:
- 模板:把每个模型的表单放在模板中,但要确保每个表单的动作属性指向不同的视图。
- URL配置:在你的urls.py文件中,为每个表单的动作标签设置正确的URL映射。
- 视图:创建的视图总数等于这个公式:1 + n,其中n是你想处理的表单数量,而“1”指的是你的实际页面。 为模板中的每个表单创建一个单独的视图。在这“n”个视图中,使用文档中看到的所有标准内容(比如
request.method == 'post'
和yourform.is_valid()
)。但关键是,在处理完表单后,你需要使用redirect('view:name')
!如果你只是想硬编码视图,也可以用redirect('/view/name/')
。使用这个重定向函数,系统不会将任何数据传回它调用的主页面视图(上面提到的“1”)。
views.py
@login_required()
def index(request):
rhi_model_form = RhiModelForm()
activity_model_form = ActivityModelForm()
rhi_list = Rhi.objects.all()
activity_list = Activity.objects.all()
context = {
'rhi_model_form': rhi_model_form,
'rhi_list': rhi_list,
'activity_model_form': activity_model_form,
'activity_list': activity_list,
}
return render(request, 'ec/index.html', context)
@login_required()
def add_rhi(request):
if request.method == 'POST':
rForm = RhiModelForm(request.POST)
if rForm.is_valid():
rForm.save()
return redirect('ec:index')
else:
rhi_errors = rForm.errors
rhi_model_form = RhiModelForm()
context = {
'rhi_errors': rhi_errors,
'rhi_model_form': rhi_model_form,
}
return render(request, 'ec/index.html', context)
@login_required()
def add_activity(request):
if request.method == 'POST':
aForm = ActivityModelForm(request.POST)
if aForm.is_valid():
activity = aForm.save(commit=False)
activity.lastModifiedBy = request.user
activity.createdBy = request.user
activity.save()
return redirect('ec:index')
else:
activity_errors = aForm.errors
activity_model_form = ActivityModelForm()
context = {
'activity_errors': activity_errors,
'activity_model_form': activity_model_form,
}
return render(request, 'ec/index.html', context)
forms.py
class RhiModelForm(forms.ModelForm):
class Meta:
model = Rhi
exclude = ['slug']
class ActivityModelForm(forms.ModelForm):
class Meta:
model = Activity
exclude = ['createdBy','lastModifiedBy']
模板(在我的例子中是 - index.html)
{% if rhi_model_form %}
{% if error_message %}<p><strong>{{error_message}}</strong></p>{% endif %}
{% if rhi_errors %}{{ rhi_errors }} {% endif %}
<form action="{% url 'ec:add_rhi' %}" method="post">
{% csrf_token %}
{{ rhi_model_form }}
<input type="submit" value="Add RHI"/>
</form>
{% endif %}
{% if activity_model_form %}
{% if error_message %}<p><strong>{{error_message}}</strong></p>{% endif %}
{% if activity_errors %}{{ activity_errors }} {% endif %}
<form action="{% url 'ec:add_activity' %}" method="post">
{% csrf_token %}
{{ activity_model_form.non_field_errors }}
{{ activity_model_form }}
<input type="submit" value="Add Activity"/>
</form>
{% endif %}
总结一下,核心概念有:
- POST:除非你想在服务器上更改东西,否则不要使用这种方法。基本上,除非该视图会处理用户的数据,否则不要在任何视图中包含、检查等“POST”的代码。在这个例子中,这就是为什么在索引视图中看不到任何关于“POST”的内容。
- 重定向/渲染:这两个快捷方式非常重要。重定向会将代码流程发送到其参数中引用的视图,并且不会传递上下文。在我的例子中,这正是我想要的。渲染不接受视图作为参数,应该用于“发送”数据,这里是发送到模板。
- 附注:在模板中,我用“if”控制包裹每个表单,因为如果当前提交的表单有错误,我不想向最终用户显示一个不同表单的单独“提交”按钮。(因为一个表单的动作调用一个独特的视图,并且如果它在视图中没有通过验证,我会重新加载失败的表单。)