Django ListView同时处理排序、筛选和分页时出现KeyError问题求助
Django ListView同时处理排序、筛选和分页时出现KeyError问题求助
嗨,我看了你的问题,核心问题其实很常见——分页跳转时丢失了筛选和排序的GET参数,导致你的filter函数尝试访问不存在的city键,抛出了KeyError。下面给你一步步的解决方案:
1. 先修复分页链接,保留所有当前查询参数
你现在的分页链接只带了page参数,比如?page=2,但跳转后之前的筛选(比如city)、排序参数都没了,request.GET里就没有这些字段,自然会触发KeyError。
方案一:在视图里处理查询参数,传递给模板
修改你的get_context_data方法,生成一个排除了page的查询参数字符串,方便模板复用:
def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) # 复制当前GET参数,移除page字段,用于分页链接 get_params = self.request.GET.copy() get_params.pop('page', None) context['query_params'] = get_params.urlencode() # 用当前GET参数初始化表单,保留用户输入的筛选条件 context["form"] = SearchForm(self.request.GET or None) return context
然后在模板的分页部分,把这个参数拼到链接里:
<ul class="pagination"> {% if listings.has_previous %} <li class="page-item"> <a class="page-link" href="?{% if query_params %}{{ query_params }}&{% endif %}page=1">« first</a> </li> <li class="page-item"> <a class="page-link" href="?{% if query_params %}{{ query_params }}&{% endif %}page={{ listings.previous_page_number }}">previous</a> </li> {% endif %} <span class="current">Page {{ listings.number }} of {{ listings.paginator.num_pages }}.</span> {% if listings.has_next %} <li class="page-item"> <a class="page-link" href="?{% if query_params %}{{ query_params }}&{% endif %}page={{ listings.next_page_number }}">next</a> </li> <li class="page-item"> <a class="page-link" href="?{% if query_params %}{{ query_params }}&{% endif %}page={{ listings.paginator.num_pages }}">last »</a> </li> {% endif %} </ul>
这样分页跳转时,筛选、排序的参数都会被保留,request.GET里就有city等字段了。
2. 优化查询集逻辑,做参数安全校验
即使参数都保留了,也要避免filter函数因为空值或者不存在的字段报错。修改你的get_queryset方法:
def get_queryset(self): queryset = super().get_queryset() get_data = self.request.GET.dict() # 处理筛选:只保留非空的、非分页/排序的参数 filter_data = {} for key, value in get_data.items(): if key not in ['page', 'sorting'] and value.strip(): filter_data[key] = value if filter_data: query = filter(filter_data) # 这里假设你的filter函数能处理合法的filter_data queryset = queryset.filter(query) # 处理排序:用elif更高效,避免多条件判断 sorting = self.request.GET.get("sorting") if sorting == "new": queryset = queryset.order_by("-created_at") elif sorting == "big": queryset = queryset.order_by("-apartment__area") elif sorting == "small": queryset = queryset.order_by("apartment__area") elif sorting == "expensive": queryset = queryset.order_by("-apartment__price") elif sorting == "cheap": queryset = queryset.order_by("apartment__price") # 另外,其实ListView自带分页功能,你可以直接在类里加`paginate_by = 2`,不用自己实例化Paginator # 这样ListView会自动处理分页,你只需要返回queryset就行,代码更简洁 paginator = Paginator(queryset, per_page=2) page_number = self.request.GET.get("page") listings = paginator.get_page(page_number) return listings
3. 完善表单的参数保留
- 排序表单:添加
onchange事件自动提交,并且保留当前选中状态:
<form class="mb-3" action="{% url 'listing-list' %}" method="get"> <select class="form-select w-50" name="sorting" onchange="this.form.submit()"> <option value="new" {% if request.GET.sorting == 'new' %}selected{% endif %}>Prvo najnoviji</option> <option value="popular" {% if request.GET.sorting == 'popular' %}selected{% endif %}>Prvo najpopularniji</option> <option value="big" {% if request.GET.sorting == 'big' %}selected{% endif %}>Po kvadraturi (opadajuće)</option> <option value="small" {% if request.GET.sorting == 'small' %}selected{% endif %}>Po kvadraturi (rastuće)</option> <option value="expensive" {% if request.GET.sorting == 'expensive' %}selected{% endif %}>Po ceni (opadajuće)</option> <option value="cheap" {% if request.GET.sorting == 'cheap' %}selected{% endif %}>Po ceni (rastuće)</option> </select> </form>
- 筛选表单:提交时保留排序参数,添加隐藏字段:
<form class="mb-3" action="{% url 'listing-list' %}" method="get"> {% for field in form %} <div class="row mb-3"> <label class="col-sm-4 col-form-label">{{ field.label }}</label> <div class="col-sm-8">{{ field }}</div> </div> {% endfor %} {% if request.GET.sorting %} <input type="hidden" name="sorting" value="{{ request.GET.sorting }}"> {% endif %} <input class="btn btn-primary fw-semibold" type="submit" value="Pretraži"> </form>
这样一套改下来,分页、筛选、排序的参数都会互相保留,就不会再出现KeyError了。
内容来源于stack exchange
相关产品推荐
相关产品推荐

