【问题标题】:Paginating the results of a Django forms POST request对 Django 表单 POST 请求的结果进行分页
【发布时间】:2011-01-17 00:23:53
【问题描述】:

我正在使用 Django 表单通过 POST 进行过滤/分面搜索,我想使用 Django 的分页器类来组织结果。在各个页面之间传递客户端时如何保留原始请求?换句话说,一旦我将另一个页面的 GET 请求传递回我的视图,我似乎就丢失了 POST 数据。我已经看到一些建议使用 AJAX 仅刷新页面的结果块,但我想知道是否有 Django-native 机制来执行此操作。

谢谢。

【问题讨论】:

    标签: python django django-forms pagination


    【解决方案1】:

    如果您想在以后的请求中访问存储数据,则必须将其存储在某个地方。 Django 提供了几种归档方式:

    1) 您可以使用sessions 来存储查询: 每个访问您的网站的访问者都会得到一个空的会话对象,您可以在该对象中存储您想要的任何内容,这就像一个字典。缺点:单个访问者不能同时进行多个分页搜索。

    2) 使用cookies: 如果您设置一个cookie 存储在客户端,浏览器会将cookie 的数据附加到您可以访问的每个请求中。 Cookie 对服务器更加友好,因为您不需要在服务器上为它们设置会话管理器,但存储在 cookie 中的数据对客户端是可见的(并且可编辑)。缺点:和以前一样。

    3) 使用隐藏字段:您可以在搜索结果页面上添加带有一些隐藏字段的表单并将查询存储在其中。然后,每当您提交表单时,客户端都会重新发送查询。缺点:您必须在页面上使用带有提交按钮的表单进行分页(简单的链接不起作用)。

    4) 创建包含查询的链接: 除了使用 POST,您还可以使用 GET。例如,您可以有一个类似"/search/hello+world/?order=votes" 的链接和类似"/search/hello+world/2/?order-votes" 的“分页链接”。然后可以很容易地从 URL 中检索查询。缺点:您可以通过 GET 发送的最大数据量是有限的(但这对于简单的搜索来说应该不是问题)。

    5) 使用组合:您可能希望将所有数据存储在会话或数据库中,并通过生成的密钥访问它们,您可以将其放入 URL 中。然后 URL 可能看起来像“/search/029af239ccd23/2"(用于第二页),您可以使用密钥访问您之前存储的大量数据。这消除了解决方案 1 和解决方案 4 的缺点。新缺点:工作量很大:)

    6) 使用 AJAX: 使用 ajax,您可以将数据存储在客户端的一些 js 变量中,然后可以传递给其他请求。而且由于 ajax 只会更新您的结果列表,因此变量不会丢失。

    【讨论】:

    • 谢谢,这很有帮助。只是为了进一步解开这个问题:这是分页器类的预期用途吗?我的视图处理初始搜索表单,然后将模板发送到第一页的 paginator.page() 对象。结果列表是从该页面的 object_list 生成的。似乎很奇怪,我无法将整个搜索结果集发送给它,并且在不重新提交每个页面的搜索的情况下以某种方式翻页。如果这是该课程的预期用途,我可以使用它。只是想确保我没有遗漏一些明显的东西。谢谢!
    • 是的,这就是预期用途。不要忘记 Django 是一个 Web 框架,从本质上讲,Web 请求是无状态的。因此,如果您想保持状态,则必须将其存储在某个地方 - tux21b 为您提供了一些关于位置的选项。
    【解决方案2】:

    从 tux21b 阅读了非常好的答案,我决定实现第一个选项,即使用会话来存储查询。这是一个搜索房地产数据库的应用程序。这是查看代码(使用 django 1.5):

    def main_search(request):
        search_form = UserSearchForm()
        return render(request, 'search/busca_inicial.html', {'search_form': search_form})
    
    
    def result(request):
        if request.method == 'POST':
            search_form = UserSearchForm(request.POST)
            if search_form.is_valid():
                # Loads the values entered by the user on the form. The first and the second
                # are MultiChoiceFields. The third and fourth are Integer fields
                location_query_list = search_form.cleaned_data['location']
                realty_type_query_list = search_form.cleaned_data['realty_type']
                price_min = search_form.cleaned_data['price_min']
                price_max = search_form.cleaned_data['price_max']
                # Those ifs here populate the fields with convenient values if the user
                # left them blank. Basically the idea is to populate them with values
                # that correspond to the broadest search possible.
                if location_query_list == []:
                    location_query_list = [l for l in range(483)]
                if realty_type_query_list == []:
                    realty_type_query_list = [r for r in range(20)]
                if price_min == None:
                    price_min = 0
                if price_max == None:
                    price_max = 100000000
                # Saving the search parameters on the session
                request.session['location_query_list'] = location_query_list
                request.session['price_min'] = price_min
                request.session['price_max'] = price_max
                request.session['realty_type_query_lyst'] = realty_type_query_list
        # making a query outside the if method == POST. This logic makes the pagination     possible.
        # If the user has made a new search, the session values would be updated. If not,
        # the session values will be from the former search. Of course, that is what we want  because
        # we want the 'next' and 'previous' pages correspond to the original search
        realty_list_result =    FctRealtyOffer.objects.filter(location__in=request.session['location_query_list']
                                                        ).filter(price__range=(request.session['price_min'], request.session['price_max'])
                                                       ).filter(realty_type__in=request.session['realty_type_query_lyst'])
        # Here we pass the list to a table created using django-tables2 that handles sorting
        # and pagination for us
        table = FctRealtyOfferTable(realty_list_result)
        # django-tables2 pagination configuration
        RequestConfig(request, paginate={'per_page': 10}).configure(table)
    
        return render(request, 'search/search_result.html', {'realty_list_size': len(realty_list_result),
                                                          'table': table})
    

    希望对您有所帮助!如果有人有任何改进建议,欢迎。

    【讨论】:

      【解决方案3】:

      作为@rvnovaes,一种使用会话来解决问题的方法。

      他的解决方案的缺点是,如果搜索字段很多,你必须写很多行代码,而且如果你在结果页面中显示搜索表单,所有的字段都是空白的,而他们应该保持他们的价值观。

      所以我宁愿将所有发布的数据保存在会话中,如果定义了会话,则在视图的开头强制 request.POST 和 request.method 的值:

      """ ... """
      if not request.method == 'POST':
          if 'search-persons-post' in request.session:
              request.POST = request.session['search-persons-post']
              request.method = 'POST'
      
      if request.method == 'POST':
          form = PersonForm(request.POST)
          request.session['search-persons-post'] = request.POST
          if form.is_valid():
              id = form.cleaned_data['id']
      """ ... """
      

      更多信息here

      【讨论】:

        【解决方案4】:

        我在我的 web 应用程序中使用 get 参数做到了这一点也许我可以帮助你:

        Views.py

        class HomeView(ListView):
        model = Hotel
        template_name = 'index.html'
        paginate_by = 10  # if pagination is desired
        
        def get_queryset(self):
           qs = super().get_queryset()
           kwargs = {}
           if 'title' in self.request.GET:
               title = self.request.GET.get('title')
               if title != '':
                   kwargs['title__icontains'] = title
           if 'category' in self.request.GET:
               category = self.request.GET.get('category')
               if category:
                   kwargs['category_id'] = category
           if 'size' in self.request.GET:
               size = self.request.GET.get('size')
               if size:
                   kwargs['size_id'] = size
           if 'service' in self.request.GET:
               service = self.request.GET.get('service')
               if service:
                   kwargs['service_id'] = service
           if 'ownership' in self.request.GET:
               ownership = self.request.GET.get('ownership')
               if ownership:
                   kwargs['ownership_id'] = ownership
           qs = qs.filter(**kwargs)
           return qs
        
        def get_context_data(self, **kwargs):
            context = super().get_context_data(**kwargs)
            form_init = {}
            form = SearchForm()
            if self.request.GET.items():
                try:
                    parameters = self.request.GET.items()
                except KeyError:
                    parameters = {}
                for key, value in parameters:
                    for field in form.fields:
                        if key == field:
                            form_init[key] = value
                form.initial = form_init
            if 'title' in self.request.GET:
               title = self.request.GET.get('title')
               if title != '':
                   context.update({
                    'title': title
                   })
            if 'category' in self.request.GET:
               category = self.request.GET.get('category')
               context.update({
                'category': category
               })
            if 'size' in self.request.GET:
               size = self.request.GET.get('size')
               context.update({
                   'size': size
              })
            if 'service' in self.request.GET:
               service = self.request.GET.get('service')
               context.update({
                   'service': service
              })
            if 'ownership' in self.request.GET:
               ownership = self.request.GET.get('ownership')
               context.update({
                  'ownership': ownership
               })
            context.update({
                'search_form': form
            })
            return context
        

        分页文件 html

        <div class="row">
          {% if is_paginated %}
          <nav aria-label="...">
            <ul class="pagination">
              {% if page_obj.has_previous %}
                <li class="page-item"><a class="page-link" href="?category={{category}}&size={{size}}&service={{service}}&ownership={{ownership}}&page={{ page_obj.previous_page_number }}">Previous</a></li>
              {% else %}
                <li class="page-item disabled"><span class="page-link">Previous</span></li>
              {% endif %}
              <span class="page-current">
                       Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
                   </span>
              {% if page_obj.has_next %}
                <li class="page-item"><a class="page-link" href="?category={{category}}&size={{size}}&service={{service}}&ownership={{ownership}}&page={{ page_obj.next_page_number }}">Next</a></li>
              {% else %}
                <li class="page-item disabled"><span class="page-link">Next</span></li>
              {% endif %}
            </ul>
          </nav>
         {% endif %}
        </div>
        

        【讨论】:

          【解决方案5】:

          您可以询问请求对象是否是 ajax,只需 request.is_ajax。通过这种方式,您可以检测到是第一次发帖请求还是关于下一页的进一步问题。

          【讨论】:

            【解决方案6】:

            将搜索表单和结果显示在一个 django 模板上。最初,使用 css 隐藏结果显示区域。在发布表单时,您可以检查搜索是否返回任何结果,如果结果存在,则使用 css 隐藏搜索表单。如果结果不存在,像以前一样使用 css 隐藏结果显示区域。在您的分页链接中,使用 javascript 提交表单,这可以像 document.forms[0].submit(); return false; 一样简单

            您需要处理如何将页码传递给 django 的分页引擎。

            【讨论】:

              【解决方案7】:

              我的建议是使用会话或 cookie 来存储发布请求。如果帖子数据是敏感的,您应该使用会话来存储它。下面的代码包含我使用会话实现它的逻辑。

              def index(request):
                  is_cookie_set = 0
                  # Check if the session has already been created. If created, get their values and store it.
                  if 'age' in request.session and 'sex' in request.session: 
                      age = request.session['age']
                      sex = request.session['sex']
                      is_cookie_set = 1
                  else:
                      # Store the data in the session object which can be used later
                      request.session['age'] = age
                      request.session['sex'] = sex
                  if(request.method == 'POST'):
                      if(is_cookie_set == 0): # form submission by the user
                          form = EmployeeForm(request.POST)
                          sex = form.cleaned_data['sex']
                          age = form.cleaned_data['age']
                          if form.is_valid():
                              result = Employee.objects.all(sex=sex,age_gte=age) # filter all employees based on sex and age
                      else: # When the session has been created
                          result = Employee.objects.all(sex=sex,age_gte=age)
                      paginator = Paginator(result, 20) # Show 20 results per page
                      page = request.GET.get('page')
                      r = paginator.get_page(page)
                      response = render(request, 'app/result.html',{'result':result})    
                      return response
                  else:
                      form = EmployeeForm()
                  return render(request,'app/home.html',{'form':form})
              

              您还应该检查帖子字段是否为空并根据它更改逻辑。您还可以按照@abidibo 的建议将整个发布请求存储在会话中。

              您也可以使用 cookie。我已经解释过了here

              【讨论】:

                【解决方案8】:

                下面的代码正在运行,第一个请求是 GET 请求,它访问表单,将直接转到 else 块。 一旦用户提出搜索查询,就会显示结果,这将是一个发布请求,第二个如果块将被激活,这个请求我们将存储在一个会话中。 当用户访问第二个搜索页面时,这将是一个 GET 请求,但我们正在检查是否有一个活动的分页会话,并检查它是否不是 GET 的页面请求。此时将触发第一个 if 块。

                def search(request):
                    if not request.method == "POST" and 'page' in request.GET:
                    if 'search-query' in request.session:
                        request.POST = request.session['search-query']
                        request.method = 'POST'
                
                    if request.method == 'POST':
                    form = Search_form(request.POST)
                    request.session['search-query'] = request.POST
                    if form.is_valid():
                        search_query = form.cleaned_data.get('search_query')
                        search_parameter = form.cleaned_data.get('search_parameter')
                        print(search_query, search_parameter)
                        queryset_list = CompanyRecords.objects.filter(**{f'{search_parameter}__icontains': search_query}).exclude(
                            company_name__isnull=True).exclude(description__isnull=True).exclude(phones__isnull=True).exclude(
                            emails__isnull=True)[:5]
                        page = request.GET.get('page', 1)
                        paginator = Paginator(queryset_list, 2)
                
                        try:
                            queryset = paginator.page(page)
                        except PageNotAnInteger:
                            queryset = paginator.page(1)
                        except EmptyPage:
                            queryset = paginator.page(paginator.num_pages)
                
                        return render(request, 'search/search_results.html', {'queryset': queryset})
                
                    else:
                    context = {
                        'form': Search_form()
                    }
                    return render(request, 'search/search.html', context)
                

                【讨论】:

                  猜你喜欢
                  • 2021-06-08
                  • 1970-01-01
                  • 1970-01-01
                  • 2019-07-11
                  • 1970-01-01
                  • 2013-05-04
                  • 2017-10-13
                  • 1970-01-01
                  • 2022-12-20
                  相关资源
                  最近更新 更多