Django中如何将一个视图生成的筛选Queryset传递到另一个视图以实现分页展示订单列表?
Django中如何将一个视图生成的筛选Queryset传递到另一个视图以实现分页展示订单列表?
嘿,我太懂你现在的困扰了——在POST请求里辛辛苦苦筛选出了订单Queryset,结果没法直接通过重定向传给分页视图,对吧?其实核心问题是Queryset没法被序列化,没法通过URL或者会话直接传递。咱们换个思路:别传Queryset本身,而是把筛选条件传递过去,让list_orders视图根据这些条件重新生成Queryset,这样分页的GET请求就能正常工作了。
下面一步步给你说具体怎么改:
第一步:改造search_orders视图,传递筛选条件而非Queryset
原来的视图是直接渲染模板,现在改成收集所有有效筛选条件,把它们转成URL查询参数,然后重定向到list_orders。这样分页时的每一页请求都会带上这些筛选条件,保证结果一致。
from django.http import HttpResponseRedirect from django.urls import reverse from urllib.parse import urlencode import datetime @login_required def search_orders(request): if request.method == 'POST' and len(request.POST) > 1: filter_params = {} msg = '' fields_selected = False # 逐个处理表单字段,收集有效筛选条件 orderid = request.POST.get('orderid') if orderid: filter_params['orderid'] = orderid msg += f'orderid - {orderid} and ' fields_selected = True bookingtype = request.POST.get('bookingtype') if bookingtype: filter_params['bookingtype'] = bookingtype msg += f'bookingtype - {bookingtype} and ' fields_selected = True # 处理日期范围字段 booked1 = request.POST.get('booked1') if booked1: filter_params['booked1'] = booked1 dt = datetime.datetime.strptime(booked1, "%Y-%m-%d").strftime("%d-%b-%y") msg += f'booked1 - {dt} and ' fields_selected = True booked2 = request.POST.get('booked2') if booked2: filter_params['booked2'] = booked2 msg += f'booked2 - {booked2} and ' fields_selected = True # 处理外键字段(比如国家编码) origin_code = request.POST.get('origin') if origin_code: filter_params['origin'] = origin_code msg += f'origin - {origin_code} and ' fields_selected = True # 剩下的字段(shp、con、matavail等)都按这个逻辑处理 if fields_selected: # 把筛选条件转成URL查询字符串 query_string = urlencode(filter_params) # 把搜索提示存到session里,避免长字符串出现在URL中 request.session['search_msg'] = f'Search criteria: {msg[:-5]}' # 重定向到list_orders,带上筛选参数 return HttpResponseRedirect(f"{reverse('tracker:list_orders')}?{query_string}") else: messages.success(request, "No field selections entered") return redirect('tracker:search_orders') else: return render(request, 'tracker/search_orders.html')
第二步:改造list_orders视图,根据筛选条件重新生成Queryset
现在这个视图要兼容两种情况:一种是原来的预设动作(比如today、open),另一种是从搜索页过来的筛选请求。它会从GET参数里读取筛选条件,重新查询数据库,再做分页。
from django.core.paginator import Paginator from django.utils import timezone @login_required def list_orders(request): orders = Order.objects.all() action_msg = '' # 处理预设动作(比如today、open) action = request.GET.get('action') if action == 'today': today = timezone.now().date() orders = orders.filter(booked=today) action_msg = 'Today\'s Orders' elif action == 'open': orders = orders.exclude(closed=True).order_by('-booked') action_msg = 'Open Orders' else: # 处理搜索筛选的情况 filter_params = request.GET.dict() # 移除分页参数,避免干扰筛选逻辑 filter_params.pop('page', None) action_msg = request.session.pop('search_msg', 'Filtered Orders') # 根据参数构建筛选Queryset if 'orderid' in filter_params and filter_params['orderid']: orders = orders.filter(orderid__icontains=filter_params['orderid']) if 'bookingtype' in filter_params and filter_params['bookingtype']: orders = orders.filter(bookingtype__icontains=filter_params['bookingtype']) # 处理日期范围 if 'booked1' in filter_params and filter_params['booked1']: orders = orders.filter(booked__gte=filter_params['booked1']) if 'booked2' in filter_params and filter_params['booked2']: orders = orders.filter(booked__lte=filter_params['booked2']) # 处理外键字段 if 'origin' in filter_params and filter_params['origin']: origin = Country.objects.filter(countrycode=filter_params['origin']).first() if origin: orders = orders.filter(origin=origin) # 剩下的字段按同样逻辑处理 # 统一排序 orders = orders.order_by('-orderid') # 分页逻辑 paginator = Paginator(orders, 16) page_number = request.GET.get('page', 1) orders_page = paginator.get_page(page_number) # 显示搜索提示消息 if action not in ['today', 'open']: messages.success(request, action_msg) return render(request, 'tracker/list_orders.html', { 'orders': orders_page, 'action': action_msg })
第三步:修改URL配置
原来的list_orders路由不需要接收action作为URL参数了,改成从GET参数获取:
app_name = 'tracker' urlpatterns = [ path('search/', search_orders, name='search_orders'), path('list/', list_orders, name='list_orders'), # 如果需要保留原来的预设动作路由,可以加下面这条,然后在视图里兼容 # path('list/<str:action>/', list_orders, name='list_orders_action'), ]
第四步:修复分页链接,保留筛选条件
别忘了修改list_orders.html里的分页代码,确保点击页码时会带上所有筛选参数,不然分页后筛选条件就丢了:
<div class="pagination"> <span class="step-links"> {% if orders.has_previous %} <a href="?page={{ orders.previous_page_number }}&{{ request.GET.urlencode }}">previous</a> {% endif %} <span class="current"> Page {{ orders.number }} of {{ orders.paginator.num_pages }}. </span> {% if orders.has_next %} <a href="?page={{ orders.next_page_number }}&{{ request.GET.urlencode }}">next</a> {% endif %} </span> </div>
为什么原来的方法不行?
你之前尝试直接把Queryset传给重定向,这本质上是行不通的——Queryset是数据库查询的“蓝图”,不是具体数据,没法序列化后通过URL或会话传递。就算你强行把所有订单对象存到会话里,数据量大的时候性能会崩,而且完全没必要。传递筛选条件让目标视图重新查询,才是符合Django设计思路的正确做法。
内容来源于stack exchange
相关产品推荐
相关产品推荐

