Django中CreateView表单验证失败时重定向至ListView并携带错误信息
解决Django表单验证失败时重定向到ListView并显示错误信息的问题
问题场景
我用Django的ListView展示库存列表,同时在列表页嵌入了一个由CreateView处理的添加表单。当表单验证通过时功能正常,但验证失败时会自动跳转到inventory_form.html模板,由于我没有创建这个模板,导致出现TemplateDoesNotExist错误。我不想创建这个额外的模板,请问如何让表单验证失败时回到ListView页面并显示错误信息?
现有代码
views.py
class InventoryListView(ListView): context_object_name = 'inventorys' model = models.Inventory def get_context_data(self, **kwargs): context = super(InventoryListView, self).get_context_data(**kwargs) context['form'] = InventoryForm() return context class InventoryCreateView(CreateView): fields = ('name', 'sn', 'desc', 'employee') model = models.Inventory
models.py
class Inventory(models.Model): name = models.CharField(max_length=255) sn = models.DecimalField(max_digits=20, decimal_places=0) desc = models.TextField(blank=True, null=True, default='Empty') employee = models.ForeignKey(Employee, null=True, on_delete=models.SET_NULL, related_name='employee') created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True, null=True) def __str__(self): return self.name def get_absolute_url(self): return reverse("system:inventory_list")
urls.py
url(r'^invn/$',views.InventoryListView.as_view(), name='inventory_list'), url(r'^invn/create$',views.InventoryCreateView.as_view(), name='inventory_create'),
inventory_list.html
{% for inventory in inventorys %} <td>{{ inventory.name }}</td> {% endfor %} <form method="post" action="{% url 'system:inventory_create' %}"> {% csrf_token %} {{ form.as_p }} <button type="submit">添加库存</button> </form>
解决方案
嘿,这个问题我之前也碰到过,其实不用额外创建模板,只要调整一下InventoryCreateView的逻辑就能解决,给你两种实用的方案:
方案一:重写form_invalid方法,渲染列表页模板
这种方法会在表单验证失败时,直接调用ListView的上下文逻辑,把带错误信息的表单传入列表页模板,避免跳转到默认的form模板。
修改views.py中的InventoryCreateView:
from django.urls import reverse_lazy class InventoryCreateView(CreateView): fields = ('name', 'sn', 'desc', 'employee') model = models.Inventory success_url = reverse_lazy('system:inventory_list') # 验证成功后跳回列表页 def form_invalid(self, form): # 实例化ListView并复用它的上下文数据 list_view = InventoryListView() list_view.request = self.request context = list_view.get_context_data() # 把验证失败的表单替换上下文里的空表单 context['form'] = form # 渲染列表页模板 return self.render_to_response(context)
解释:
success_url用reverse_lazy是因为类视图在加载时,URL配置还未完全初始化,直接用reverse会报错。- 复用
InventoryListView的get_context_data方法,确保列表页的原有数据(比如库存列表)能正常显示。 - 把验证失败的表单传入上下文,这样页面就能显示错误提示和用户之前输入的内容。
方案二:直接指定CreateView的模板为列表页
这种方法更直接,让CreateView在验证失败时直接使用列表页模板,同时手动补充列表数据到上下文里。
修改views.py中的InventoryCreateView:
from django.urls import reverse_lazy class InventoryCreateView(CreateView): fields = ('name', 'sn', 'desc', 'employee') model = models.Inventory template_name = 'inventory_list.html' # 指定使用列表页模板 success_url = reverse_lazy('system:inventory_list') def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) # 补充库存列表数据到上下文,确保页面能显示现有库存 context['inventorys'] = models.Inventory.objects.all() return context
解释:
template_name指定验证失败时渲染的模板为inventory_list.html,代替默认的inventory_form.html。- 在
get_context_data中手动添加inventorys数据,因为CreateView默认不会提供ListView的列表数据。 - 验证成功后通过
success_url跳回列表页,验证失败则直接在列表页显示错误表单。
两种方案都能达到你的需求,选哪种看你个人习惯——方案一复用了ListView的逻辑,更符合DRY原则;方案二更直观,代码量更少。
内容的提问来源于stack exchange,提问作者Dicky Raambo
相关产品推荐
相关产品推荐

