[ACCEPTED]-Paginating the results of a Django forms POST request-pagination

Accepted answer
Score: 28

If you want to access the store data in 42 later request, you would have to store it 41 somewhere. Django provides several ways 40 to archive this:

1) You can use sessions to store the query: Every visitor who visits 39 your site will get an empty session object 38 and you can store whatever you want inside 37 this object, which acts like a dict. Drawback: A 36 single visitor can't do multiple searches 35 with pagination concurrently.

2) Use cookies: If you set 34 a cookie which is stored on the client side, the 33 browser will append the data of the cookie 32 to each request where you can access it. Cookies 31 are more server friendly, because you don't 30 need a session manager for them on the server, but 29 the data stored in cookies is visible (and 28 editable) to the client. Drawback: same 27 as before.

3) Use hidden fields: You can add a form with some 26 hidden fields on your search-result page 25 and store the query inside them. Then, the 24 client will resend the query whenever you 23 submit the form. Drawback: You must use 22 a form with submit buttons for the pagination 21 on your page (simple links wont work).

4) Create Links which contain the query: Instead 20 of using POST, you can also use GET. For 19 example, you could have a link like "/search/hello+world/?order=votes" and 18 "paginated links" like "/search/hello+world/2/?order-votes". Then 17 the query can be easily retrieved from the 16 URL. Drawback: The maximum amount of data 15 you can send via GET is limited (But that 14 shouldn't be a problem for a simple search).

5) Use a combination: You 13 might want to store all the data in a session 12 or a database and access them via a generated 11 key which you can put in the URL. URLs might 10 then look like "/search/029af239ccd23/2" (for the 2nd page) and 9 you can use the key to access a huge amount 8 of data which you have stored before. This 7 eliminates the drawback of solution 1 as 6 well as that of solution 4. New drawback: much 5 work :)

6) Use AJAX: With ajax you can store the data 4 inside some js-variables on the client side, which 3 can then passed to the other requests. And 2 since ajax will only update your result 1 list, the variables aren't getting lost.

Score: 8

Reading the very nice answer from tux21b 6 I decided to implement the first option, i.e., to 5 use the session to store the query. This 4 is an application that searches real estate 3 databases. Here is the view code (using 2 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})

Hope it helps!If anyone has 1 any improvement to suggest, be welcome.

Score: 6

As @rvnovaes, a way to use session to solve 10 the matter.

The drawback of his solution 9 is that if there are many search fields 8 you have to write many lines of code, and 7 also if you show the search form in the 6 result page, all the fields will be blank, while 5 they should keep their values.

So I'd rather 4 save all the post data in session, and at 3 the beginning of the view force the value 2 of request.POST and request.method if a 1 session is defined:

""" ... """
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']
""" ... """

More info here

Score: 2

I did this in my web application with get 1 parameters Maybe i can help you :

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

Pagination file 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>
Score: 0

You can ask request object if it's ajax, simply 3 request.is_ajax. This way you can detect, whether it's 2 first post request or further questions 1 about the next pages.

Score: 0

Have the search form and the results display 11 on one single django template. Initially, use 10 css to hide the results display area. On 9 POSTing the form, you could check to see 8 if the search returned any results and hide 7 the search form with css if results exist. If 6 results do not exist, use css to hide the 5 results display area like before. In your 4 pagination links, use javascript to submit 3 the form, this could be as simple as document.forms[0].submit(); return false;

You 2 will need to handle how to pass the page 1 number to django's paging engine.

Score: 0

My suggestion would be to store the post 10 request using a session or a cookie. In 9 case the post data is sensitive, you should 8 use session to store it. The code below 7 contains my logic to implement it using 6 session.

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})

You should also check if the post 5 fields are empty or not and change the logic 4 according to it. You can also store the 3 whole post request in the session as suggested 2 by @abidibo.

You can also use cookies for 1 the same. I have explained it here

Score: 0

The Below code is working, the first request 12 is a GET request, which accesses the form, will 11 go directly to the else block. Once the 10 user puts up a search query, results will 9 be shown, which will be a post request, and 8 2nd if block will be activated, this request 7 we will store in a session. When the user 6 accesses 2nd search page, it will be a GET 5 request, but we are checking whether there 4 is an active pagination session, and also 3 checking whether it's not a page request 2 of GET. At this point 1st if block will 1 be trigerred.

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)

More Related questions