Django类视图中调用函数的return render无法中断视图执行
我使用基于类的ListView(CB ListView)展示对象,希望在ListView执行过程中,基于另一个模型的PK设置会话变量:
views.py
class ProduitListView(LoginRequiredMixin, ListView): model = Produit context_object_name = "produits" paginate_by = 10 template_name = 'products/produits.html' ordering = ['-mageid', ] def get_context_data(self, *args, **kwargs): context = super(ProduitListView, self).get_context_data( *args, **kwargs) # used for incoming products (sourcing cf URLS) supplier_pk = self.kwargs.get('pk', None) if supplier_pk: set_incoming_supplier(self.request, supplier_pk) context['avail_warehouses'] = Warehouse.objects.all() context['js_warehouses'] = serialize( 'json', Warehouse.objects.all(), fields=('code', 'id', )) context['title'] = 'Produits' return context
set_incoming_supplier(在另一个APP中)
@login_required def set_incoming_supplier(request, pk): supplier = Supplier.objects.filter(pk=pk).first() supp = SupplierSerializer(instance=supplier).data rs = request.session if 'income' in rs: if 'cur_supplier' in rs['income']: prev_supplier = rs['income']['cur_supplier'] if supp != prev_supplier: return render(request, 'sourcing/alert_supplier_change.html', {'prev_supplier': prev_supplier, 'cur_supplier': rs['income']['cur_supplier']}) rs['income'] = {'cur_supplier': supp}
我原本认为上述代码中的return render(request, 'sourcing/alert_supplier_change...可以中断ListView的执行,渲染警告页面,但实际并未生效,ListView仍继续执行并最终渲染ProduitListView页面。请问这是为什么?
你调用set_incoming_supplier时,只是执行了这个函数,但完全没有处理它返回的HttpResponse对象。
在Django类视图中,只有从视图核心方法(比如get()、post())返回的HttpResponse才会被用作请求的最终响应。你现在是在get_context_data里调用这个函数,就算函数返回了警告页面的响应,这个返回值也被直接丢弃了——get_context_data的职责是准备模板上下文,它会继续执行后续代码,最终返回上下文数据,让ListView正常渲染默认页面。
另外,get_context_data本来就不该处理请求跳转或响应渲染这类逻辑,这违背了类视图的职责划分原则。
要实现中断ListView并渲染警告页面的效果,你需要把供应商检查逻辑移到视图的get()方法中——get()是类视图处理GET请求的入口,它返回的HttpResponse会直接作为请求响应:
修改ProduitListView类
class ProduitListView(LoginRequiredMixin, ListView): # 原有属性保持不变 model = Produit context_object_name = "produits" paginate_by = 10 template_name = 'products/produits.html' ordering = ['-mageid', ] def get(self, request, *args, **kwargs): supplier_pk = self.kwargs.get('pk', None) if supplier_pk: # 调用函数并接收返回值 response = set_incoming_supplier(request, supplier_pk) # 如果返回了HttpResponse(即需要显示警告),直接返回这个响应 if response: return response # 无警告需求时,执行ListView原有的get逻辑 return super().get(request, *args, **kwargs) def get_context_data(self, *args, **kwargs): context = super().get_context_data(*args, **kwargs) # 仅保留上下文准备逻辑 context['avail_warehouses'] = Warehouse.objects.all() context['js_warehouses'] = serialize( 'json', Warehouse.objects.all(), fields=('code', 'id', )) context['title'] = 'Produits' return context
调整set_incoming_supplier函数
确保在不需要警告时返回None,让视图继续执行原有逻辑:
@login_required def set_incoming_supplier(request, pk): supplier = Supplier.objects.filter(pk=pk).first() if not supplier: return None supp = SupplierSerializer(instance=supplier).data rs = request.session if 'income' in rs and 'cur_supplier' in rs['income']: prev_supplier = rs['income']['cur_supplier'] if supp != prev_supplier: return render(request, 'sourcing/alert_supplier_change.html', {'prev_supplier': prev_supplier, 'cur_supplier': supp}) rs['income'] = {'cur_supplier': supp} # 无警告需求时返回None return None
这样当检测到供应商变更时,set_incoming_supplier返回警告页面的响应,get()方法直接返回这个响应,中断ListView的后续执行;如果没有变更,函数返回None,视图继续渲染产品列表页面。
内容的提问来源于stack exchange,提问作者Abpostman1

