【问题标题】:Django ListView pagination using get_queryset使用 get_queryset 的 Django ListView 分页
【发布时间】:2013-02-04 06:15:16
【问题描述】:

我一直在尝试学习并掌握在 Django 中使用分页的 ListViews,但我似乎在理解概念方面遇到了一些麻烦。所以,让我展示一些代码 - 我的 view.py 看起来像这样:

class SearchDisplayListView(ListView):
    model = BlogPosts
    template_name = "searchres_list.html"
    paginate_by = '15'
    context_object_name = "searchres"

    def get_context_data(self, **kwargs):
        context = super(SearchDisplayListView, self).get_context_data(**kwargs)
        q = self.request.GET.get('q')
        q = q.replace(" ","+")
        context['searchq'] = q
        return context

    def get_queryset(self):
        queryset = super(SearchDisplayListView, self).get_queryset()
        # Get the q GET parameter
        q = self.request.GET.get('q')
        q = q.replace(" ","+")
        if q is None:
        # Return the base queryset
        return queryset
        # Return a filtered queryset
       ## do some awesome calculations using "q" i.e send request to search server
       ## get back a list from the search server.. example: range(0,100)
        queryset = range(0,100)
        return queryset

如您所见,我实际上并没有使用模型填充我的查询集 - 而是从我自己的“搜索服务器”返回结果 - 例如在这个示例中 range(0,100)。

现在,我正在尝试在我的模板代码中使用以下内容:

<table class="table table-hover">
<tbody>
{% for i in searchres %}
<tr>
<td><img src="http://placehold.it/140x140" class="img-polaroid"></td>
<td>{{i}}</td>
</tr>
{% endfor %}
</tbody>
</table>

我的分页如下:

{% if is_paginated %}
<div class="pagination pagination-centered">
    <ul>
        {% if page_obj.has_previous %}
<li><a href="/search/?q={{searchq}}/page={{ page_obj.previous_page_number }}">Prev</a></li>
        {% endif %}

        {% for i in paginator.page_range %}
            {% if page_obj.has_next %}
<li><a href="/search/?q={{searchq}}/page={{ page_obj.number }}">{{i}}</a></li>
            {% endif %}
        {% endfor %}

        {% if page_obj.has_next %}
<li><a href="/search/?q={{searchq}}/page={{ page_obj.next_page_number }}">Next</a></li>
        {% endif %}

    </ul>
</div>

现在,我有以下问题:

[1] 虽然模板似乎对第一组进行了分页(即 0 到 14),但我无法在后续页面中看到其他值。所以,当我点击下一页时,我得到:

 - http://mydata:8000/search/?q=myquery/page=2

当我再次点击下一页时,我得到:

 - http://mydata:8000/search/?q=boots/page=2/page=2

这显然是错误的。我看不到如何生成:

 - http://mydata:8000/search/?q=boots/page=3

[2]即使我手动设置:

 - http://mydata:8000/search/?q=boots/page=3

我再次看到从 0 到 14 的值,而不是 page:3 中的逻辑集。

此外,这基本上告诉我,在每个页面上,get_queryset 似乎都在运行,生成前 0 到 14 个值 - 这不是我想要的。

如您所见,我在 get_queryset 中有自己的自定义数据集 - 而不是任何 DB 交互 - 所以,我不确定如何对这些自定义数据进行分页。

感谢您花时间阅读我相当长的帖子!

【问题讨论】:

    标签: django django-models django-templates django-views


    【解决方案1】:

    查询参数必须用&而不是/

    分隔
    /search/?q={{searchq}}/page={{ page_obj.previous_page_number }}
    

    应该是:

    /search/?q={{searchq}}&page={{ page_obj.previous_page_number }}
    

    此外,最好使用 URL 解析器而不是硬编码 url,例如:

    {% url 'search' searchq page_obj.previous_page_number %}
    

    【讨论】:

    • 这简直太棒了。我只是花了一天时间来解决这个问题!你知道这在文档中提到的地方吗?再次感谢 - 接受您的回答。
    • 只是另一个简单的问题-如果我按照您的建议使用 url 解析器-如何设置与它对应的 urls.py?即如何将 searchq、page_obj.previous_page_number 传递给我的 urls.py?
    • @JohnJ 关于查询参数,这就是 HTTP 的工作方式,它与 django 无关。对于 URL 解析器,实际上它不适用于查询参数,但至少在第一部分您可以使用它。 {% url 'myaapp.views.search' %}?q={{searchq}}&page={{ page_obj.previous_page_number }} 将正常工作。在这种情况下,你不必在 urls.py 中做任何事情。更多细节在这里:docs.djangoproject.com/en/dev/ref/templates/builtins/…
    • 谢谢你 - 我明白你的意思 - 我想我的大脑在寻找 & !
    【解决方案2】:

    只是为了记录,代码应该是:

    {% if is_paginated %}
        <div class="pagination pagination-centered">
        <ul>
        {% if page_obj.has_previous %}
            <li><a href="/search/?q={{searchq}}&page={{ page_obj.previous_page_number}}">Prev</a></li>
        {% endif %}
    
        {% if page_obj.has_next %}
           {% for i in paginator.page_range %}
              <li><a href="/search/?q={{searchq}}&page={{ i }}">{{i}}</a></li>
            {% endfor %}
              <li><a href="/search/?q={{searchq}}&page={{ page_obj.next_page_number }}">Next</a></li>
        {% endif %}
    
    </ul>
    </div>
    

    有两个变化: 1. page_obj.number 返回实际页面,因此它被递增变量 i 替换。 2. 为清楚起见,将 for 循环移到 page_obj.has_next 内。如果您这样做,则页码链接仅在比提问者预期的页面多的情况下才会显示。如果您仍然想展示它:只需将其移出即可。

    【讨论】:

      【解决方案3】:

      在研究了这个问题一段时间并从这个条目开始后,我最终得到了一个复杂但通用的可重用解决方案。

      对于 OP 来说太过分了,但很多人似乎都有分页和搜索问题,所以这是我的 0.02 美元。

      (对于 Django 来说有点新手,所以我确信还有一些需要改进的地方,但我现在能够很快地将搜索和分页结合起来。请忽略任何关于 'db' 或 'mdb' 的内容,这高度特定于我的应用程序,它经常在 Django 数据库之外执行原始 SQL。)

      简介:

      基本上,我认为我可以用一块石头杀死两只鸟。我处理过滤行集的原因是我从表单驱动搜索。

      而且...表单能够提供重建分页 URL 所需的信息。

      因此,基本上最终得到了一个系统,该系统的工作主要包括构建一个普通的搜索表单,然后将其与 ListView 的适当子类版本连接起来。请参阅类 RoleListView。

      我还需要创建列表视图的模板,但如果你参考它,@ pssecurity/security_list.html 你会发现它非常基本。

      详情:

      关于分页的聪明之处在于类 KSearchListView(ListView)。这些东西是完全通用的,我可以在任意多个搜索页面上重复使用它。

      方法get_queryset,通过调用form.doSearch 进行数据库过滤。

      实际分页在方法 get_context_data 上,该方法检查是否存在表单、是否有效,然后通过使用表单的已清理参数重新填充它来操作分页 URL。

      还请注意,有两个传入 URL,一个未过滤的列表,一个过滤的搜索)。两者都映射到同一个 ListView。

      urls
      
          #Roles aka PSROLEDEFN
          url(r'^roles/search',
              login_required(RoleListView.as_view()),
              name="psroledefn_search"),
      
          url(r'^roles/$',
              # 'pssecurity.views.psroledefn_list',
              login_required(RoleListView.as_view()),
              name="psroledefn_list"),
      
      
      
              #generic
      class KSearchListView(ListView):
          def __str__(self):
              return self.__class__.__name__
      
          __repr__ = __str__
      
          form_class = form = None
          di_hardcoded_context = {}
      
          def setdb(self):
              #specific to my app
              self.db = self.rdb
      
          def dispatch(self,*args,**kwargs):
              #specific to my app
              if not self.mdb:
                  self.mdb = MultiDb.get(self.kwargs["dbr"])
                  self.djangodb = self.mdb.djangodb
                  self.rdb = self.mdb.rdb
                  self.setdb()
              #specific to my app
      
              return super(KSearchListView, self).dispatch(*args,**kwargs)
      
          def get_queryset(self,*args,**kwargs):
              # logging.info("%s.get_queryset(%s,%s)" % (self,args,kwargs))
      
              self.request.get = self.request.GET
      
              if self.form_class:
                  #pagination info
                  #do we have a form and are we coming from it?
                  if self.request.method == "GET":
                      self.form = self.form_class(self.db, self.request.GET)
      
                      if self.form.is_valid():
                          logging.info("form.doSearch")
                          li_c = self.form.doSearch()
                          return li_c
                      else:
                          logging.debug("not is_valid branch")
                  else:
                      self.form = self.form_class(self.mdb.rdb)
              #fetch all rows for the underlying table
              return self.fetch_all()
      
          def fetch_all(self):
              #specific to my app
              #you would probably use a <model>.objects.all()
              return list(pssys.Pssys.fetch(self.db,self.recname))
      
      
          def _override_context_data(self,context):
              pass
      
          def get_context_data(self,*args,**kwargs):
              # logging.info("%s.get_context_data(%s,%s)" % (self,args,kwargs))
      
              context = super(KSearchListView, self).get_context_data(**kwargs)
              context['form'] = self.form
              context["dbr"] = self.mdb.rdbname
      
      
              #pagination info
              #we are going to put the GET variables right back into the next/prev
              url = ""
              page_obj = context["page_obj"]
              if self.form and self.form.is_valid() and self.form.cleaned_data:
      
                  li = [self.request.path,"?"]
      
                  #driving the url assembly from the form fields and
                  #using the cleaned data should be pretty safe
                  di = self.form.cleaned_data
                  for k in self.form.fields:
                      li.append("%s=%s" % (k,di[k]))
                      li.append("&")
                  # li = li[:-1]
                  url = "".join(li)
      
              #if we didn't come in through a search form
              if not url:
                  url = "?"
      
              #now conditionally add the previous/next as appropriate. 
              #url has either a trailing ? or & at this point
              kpaging_previous_url = kpaging_next_url = ""
              if page_obj.has_previous():
                  kpaging_previous_url = context["kpaging_previous_url"] = url + "page=%s" % (page_obj.previous_page_number())
              if page_obj.has_next():
                  kpaging_next_url = context["kpaging_next_url"] = url + "page=%s" % (page_obj.next_page_number())
      
              logging.debug("url:%s" % (url))
              logging.debug("kpaging_previous_url:%s" % (kpaging_previous_url))
              logging.debug("kpaging_next_url:%s" % (kpaging_next_url))
      
              #pickup anything the subclass has set for the context
              context.update(self.di_hardcoded_context)
              self._override_context_data(context)
              return context
      
      
              #what I need to write for every list/search page...
      
      class RoleListView(KSearchListView):
      
          template_name = "pssecurity/security_list.html" 
          paginate_by = 20
          recname = "PSROLEDEFN"
          form_class = Search_PSROLEDEFN
          di_hardcoded_context = dict(
              title="Search Roles",
              header="Roles",
              templatename_inst="PSROLEDEFN_list",
              url_action='security:psroledefn_search')
      
      #pagination info the forms
      
              #generic
      class SearchForm(forms.Form):
          def __init__(self, db, request=None):
              self.db = db
              li_arg = [request] if request else []
      
              super(forms.Form, self).__init__(*li_arg)
      
          def __str__(self):
              return self.__class__.__name__
      
          __repr__ = __str__
      
      
              #what I need to write for every list/search page...
      class Search_PSROLEDEFN(SearchForm):
          ROLENAME = forms.CharField(max_length=20, required=False)
          DESCR = forms.CharField(max_length=32, required=False)
      
          status_custom = ChooseStatusCustomizationField()
          hasUsers = ChooseYesNoDontCareField("has Users?")
          hasPermissions = ChooseYesNoDontCareField("has Permissions?")
          hasPortalReferences = ChooseYesNoDontCareField("has Portal?")
      
          def doSearch(self):
      
              ROLENAME = self.cleaned_data["ROLENAME"]
              DESCR = self.cleaned_data["DESCR"].strip()
              status_custom = self.cleaned_data["status_custom"]
      
              hasPortalReferences = self.cleaned_data["hasPortalReferences"]
              hasPermissions = self.cleaned_data["hasPermissions"]
              hasUsers = self.cleaned_data["hasUsers"]
      
              #cut out a lot of code specific to my app
              #you would want to do an appropriate
              #<model>.objects.filter()...
              returns <select from my db>
      
      
      
      
      #a typical template, note that isn't even specific to an object
      #refer to class RoleListView to see how the template is built.
      #the actual details of the fetched objects are left to <templatename_inst>
      
      pssecurity/security_list.html
      {% block search %}
      <div id="block_search">
      <span>{{header}}</span>
      
      <div class="row">
      {% if form %}
      
      <div id="search" class="well col-xs-9" >
          <form class= "form-horizontal" action="{% url url_action dbr=dbr %}" method="get">
              {{form.as_table}}
              <input type="submit" value="search">
          </form>
      </div>
      {% endif %}
      {% endblock %}
      
      {%block content %}
      <div id = "block_content">
      {% for inst in object_list %}
          <div class="row">
              {% include templatename_inst %}
          </div>
      {% endfor %}
      
      {% include "websec/kpagination.html" %}
      
      </div>
      {%endblock %}
      
              #generic
      kpagination.html
      <div class="pagination">
          <span class="step-links" >
              {% if li_inst.has_previous %}
                  <a href="?page={{ li_inst.previous_page_number }}">previous</a>
              {% endif %}
      
              <span class="current" >
                  Page {{ li_inst.number }} of {{ li_inst.paginator.num_pages }}.
              </span>
      
              {% if li_inst.has_next %}
                  <a \href="?page={{ li_inst.next_page_number }}">next</a>
              {% endif %}
          </span>
      </div>
      

      【讨论】:

        【解决方案4】:

        我们可以在基于 django 类的 Listview 中进行如下操作

        views.py

        try:
            from urllib import urlencode
        except ImportError:
            from urllib.parse import urlencode
        
        class MyListView(ListView):
            # ............
            def get_context_data(self, *args, **kwargs):
                context = super(
                    GroupSubsListView, self
                ).get_context_data(*args, **kwargs)
                query_params = self.request.GET.copy()
                query_params.pop('page', None)
                context['query_params'] = urlencode(query_params)
                return context
        

        模板.html

        <!-- pagination -->
        {% if is_paginated %}
          <p>showing {{ page_obj.start_index }} to {{ page_obj.end_index }} of {{ page_obj.paginator.count }}</p>
          <div class="text-center">
            <nav>
              <ul class="pagination">
                {% if page_obj.has_previous %}
                <li class="page-item">
                  <a class="page-link" href="?page={{ page_obj.previous_page_number }}&{{ query_params }}" aria-label="Previous">
                    Previous
                  </a>
                </li>
                {% endif %}
                <li class="page-item"><a class="page-link"> Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}. </a></li>
                {% if page_obj.has_next %}
                <li class="page-item">
                  <a class="page-link" href="?page={{ page_obj.next_page_number }}&{{ query_params }}" aria-label="Next">
                    Next
                  </a>
                </li>
                {% endif %}
              </ul>
            </nav>
          </div>
        {% endif %}
          <!-- end/ -->
        

        【讨论】:

          猜你喜欢
          • 2021-08-09
          • 1970-01-01
          • 2013-04-15
          • 1970-01-01
          • 2022-10-05
          • 2020-04-01
          • 2014-11-29
          • 2019-10-18
          • 1970-01-01
          相关资源
          最近更新 更多