如何优化Django函数视图代码避免重复?新手求助
如何优化Django视图代码重复并解决提交后数据不更新问题
作为Django新手,遇到代码重复和数据更新的问题太正常了,咱们一步步拆解解决:
一、搞定代码重复:抽离通用逻辑,告别冗余
你的视图里多次重复渲染同一个模板,传递的上下文数据大部分一致,咱们把这些重复的部分抽出来,让代码清爽不少:
1. 先从函数视图入手:提取基础上下文
把所有分支都需要的通用数据(比如listing信息、当前出价、出价总数等)提前查询整理,避免反复写相同的代码:
@login_required(login_url='/login') def view_list(request, listing_id): # 提前获取所有通用数据,只查一次 listing = Listing.objects.get(pk=listing_id) bids = Bid.objects.filter(listing=listing_id).order_by('-id') current_bid = bids.first().bid if bids.exists() else None current_bid_user = bids.first().user if bids.exists() else None total_bids = bids.count() bid_user_message = "Your bid is the current bid." if request.user == current_bid_user else None # 基础上下文,所有分支直接复用 base_context = { "form": BidForm(), "total_bids": total_bids, "bid": current_bid, "listing": listing, "bid_user": bid_user_message } if request.method == "GET": return render(request, "auctions/view_list.html", base_context) else: form = BidForm(request.POST) if form.is_valid(): value = form.cleaned_data # 处理初始无出价的情况,避免报错 if bids.exists() and value['bid'] <= current_bid: # 出价过低,仅补充错误信息到上下文 base_context.update({ "error_check": True, "alert": f"Your bid is lower than the current bid ${current_bid}! Try placing a higher one." }) return render(request, "auctions/view_list.html", base_context) else: # 保存新出价 new_bid = form.save(commit=False) new_bid.user = request.user new_bid.listing = listing new_bid.save() # 用重定向解决数据更新问题(后面详细说) return redirect('view_list', listing_id=listing_id) else: # 表单验证失败,把错误表单返回给模板 base_context["form"] = form return render(request, "auctions/view_list.html", base_context)
2. 进阶尝试:用基于类的视图(CBV)减少重复
如果想进一步简化代码,Django的通用类视图能帮你封装很多重复逻辑,比如用DetailView展示列表详情,结合FormMixin处理出价表单:
from django.views.generic import DetailView from django.views.generic.edit import FormMixin from django.urls import reverse class ListingDetailView(FormMixin, DetailView): model = Listing template_name = "auctions/view_list.html" form_class = BidForm login_url = '/login' def get_context_data(self, **kwargs): # 这里添加通用上下文,和之前的base_context逻辑一致 context = super().get_context_data(**kwargs) listing = self.object bids = Bid.objects.filter(listing=listing).order_by('-id') current_bid = bids.first().bid if bids.exists() else None current_bid_user = bids.first().user if bids.exists() else None total_bids = bids.count() bid_user_message = "Your bid is the current bid." if self.request.user == current_bid_user else None context.update({ "total_bids": total_bids, "bid": current_bid, "bid_user": bid_user_message }) return context def post(self, request, *args, **kwargs): self.object = self.get_object() form = self.get_form() if form.is_valid(): return self.form_valid(form) else: return self.form_invalid(form) def form_valid(self, form): listing = self.object bids = Bid.objects.filter(listing=listing).order_by('-id') current_bid = bids.first().bid if bids.exists() else None if bids.exists() and form.cleaned_data['bid'] <= current_bid: # 出价过低,返回错误上下文 context = self.get_context_data(form=form) context.update({ "error_check": True, "alert": f"Your bid is lower than the current bid ${current_bid}! Try placing a higher one." }) return render(self.request, self.template_name, context) else: new_bid = form.save(commit=False) new_bid.user = self.request.user new_bid.listing = listing new_bid.save() return super().form_valid(form) def get_success_url(self): return reverse('view_list', kwargs={'listing_id': self.object.pk})
然后在urls.py里替换原有的视图路由:
path('listing/<int:pk>', ListingDetailView.as_view(), name='view_list'),
类视图一开始可能有点绕,但它帮你封装了获取对象、表单验证等重复逻辑,长期维护起来更省心。
二、解决提交后不显示新信息的问题
你遇到的核心问题是:提交表单后直接渲染模板,但数据查询是在处理POST之前做的,没包含新提交的出价。最优解是遵循Post/Redirect/Get(PRG)模式:
核心思路:
表单提交成功后,不要直接render模板,而是重定向到同一个页面(发起GET请求),这样页面会重新查询最新的数据,自然就能显示新出价了。
在上面优化后的函数视图里,我已经把成功提交后的render改成了redirect:
# 保存新出价后 new_bid.save() return redirect('view_list', listing_id=listing_id)
这样用户提交后,浏览器会发起新的GET请求,重新获取页面数据,最新的出价、出价总数都会被正确加载。
如果一定要直接渲染(不推荐),那需要在保存新出价后重新查询bids数据,更新上下文里的total_bids、current_bid等字段再传递给模板,但这种方式可能导致用户刷新页面重复提交表单,不如PRG模式可靠。
额外小优化
- 模板里的出价判断可以简化:
{{ bid|default:listing.price|stringformat:"1.2f" }},不用再写if-else。 - 把
bid == 0改成bids.exists(),因为QuerySet不是数字,原判断逻辑其实是错误的,exists()才是检查是否有数据的正确方式。
内容的提问来源于stack exchange,提问作者LBD_23
相关产品推荐
相关产品推荐

