You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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">&laquo; 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 &raquo;</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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.08 03:10:15