How to split Django into other get variables?

I'm having problems using pagination in Django . Take the URL below as an example:

http://127.0.0.1:8000/users/?sort=first_name 

On this page, I sort the list of users by their first name. Without a variable of type GET, by default it sorts by id.

Now, if I click the following link, I expect the following URL:

 http://127.0.0.1:8000/users/?sort=first_name&page=2 

Instead, I lose all get variables and end

 http://127.0.0.1:8000/users/?page=2 

This is a problem because the second page is sorted by id instead of first_name.

If I use request.get_full_path, I end up with an ugly URL:

 http://127.0.0.1:8000/users/?sort=first_name&page=2&page=3&page=4 

What solution? Is there a way to access the GET variables in the template and replace the value for the page?

I use pagination as described in the Django documentation , and I prefer to keep using it. The code for the template I'm using is similar to this:

 {% if contacts.has_next %} <a href="?page={{ contacts.next_page_number }}">next</a> {% endif %} 
+49
django pagination
Jan 12 2018-10-12T00:
source share
15 answers

I thought the suggested tags were too complicated, here is what I did in the template:

 <a href="?{% url_replace request 'page' paginator.next_page_number %}"> 

And tag function:

 @register.simple_tag def url_replace(request, field, value): dict_ = request.GET.copy() dict_[field] = value return dict_.urlencode() 

If url_param is not already specified in the URL, it will be added with a value. If it already exists, it will be replaced with the new value. This is a simple solution that works for me, but does not work when the URL has several parameters with the same name.

You also need the RequestContext request instance to be provided to your template from your view. More details here:

http://lincolnloop.com/blog/2008/may/10/getting-requestcontext-your-templates/

+44
May 17 '13 at 12:50
source share

I think url_replace solution can be rewritten more elegantly as

 from urllib.parse import urlencode from django import template register = template.Library() @register.simple_tag(takes_context=True) def url_replace(context, **kwargs): query = context['request'].GET.dict() query.update(kwargs) return urlencode(query) 

with a template string simplified to

 <a href="?{% url_replace page=paginator.next_page_number %}"> 

This allows you to replace multiple keys, but does not allow you to perform queries using duplicate keys.

+21
Mar 29 '16 at 15:43
source share

After some games, I found a solution ... although I don’t know if it is really good. I would prefer a more elegant solution.

In any case, I pass the request to the template and can access all GET variables through request.GET. Then I go through the GET dictionary and until the variable is printed on it.

 {% if contacts.has_previous %} <a href="?page={{ contacts.previous_page_number }}{% for key,value in request.GET.items %}{% ifnotequal key 'page' %}&{{ key }}={{ value }}{% endifnotequal %}{% endfor %}">previous</a> {% endif %} <span class="current"> Page {{ contacts.number }} of {{ contacts.paginator.num_pages }}. </span> {# I have all of this in one line in my code (like in the previous section), but I'm putting spaces here for readability. #} {% if contacts.has_next %} <a href="?page={{ contacts.next_page_number }} {% for key,value in request.GET.items %} {% ifnotequal key 'page' %} &{{ key }}={{ value }} {% endifnotequal %} {% endfor %} ">next</a> {% endif %} 
+8
Jan 12 '10 at 9:19
source share

In your views.py you will somehow get access to the criteria that you sort, for example. first_name . You will need to pass this value into the template and insert it there to remember it.

Example:

 {% if contacts.has_next %} <a href="?sort={{ criteria }}&page={{ contacts.next_page_number }}">next</a> {% endif %} 
+5
Jan 12
source share

You can create a context processor to use wherever pagination is used.

For example, in my_project/my_app/context_processors.py :

 def getvars(request): """ Builds a GET variables string to be uses in template links like pagination when persistence of the GET vars is needed. """ variables = request.GET.copy() if 'page' in variables: del variables['page'] return {'getvars': '&{0}'.format(variables.urlencode())} 

Add a context processor to your Django project settings:

 TEMPLATE_CONTEXT_PROCESSORS = ( 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', 'django.core.context_processors.i18n', 'django.core.context_processors.request', 'django.core.context_processors.media', 'django.core.context_processors.static', ... 'my_project.my_app.context_processors.getvars', ) 

Then in your templates you can use this when paginating:

 <div class="row"> {# Initial/backward buttons #} <div class="col-xs-4 col-md-4 text-left"> <a href="?page=1{{ getvars }}" class="btn btn-rounded">{% trans 'first' %}</a> {% if page_obj.has_previous %} <a href="?page={{ page_obj.previous_page_number }}{{ getvars }}" class="btn btn-rounded">{% trans 'previous' %}</a> {% endif %} </div> {# Page selection by number #} <div class="col-xs-4 col-md-4 text-center content-pagination"> {% for page in page_obj.paginator.page_range %} {% ifequal page page_obj.number %} <a class="active">{{ page }}</a> {% else %} <a href="?page={{ page }}{{ getvars }}">{{ page }}</a> {% endifequal %} {% endfor %} </div> {# Final/forward buttons #} <div class="col-xs-4 col-md-4 text-right"> {% if page_obj.has_next %} <a href="?page={{ page_obj.next_page_number }}{{ getvars }}" class="btn btn-rounded">{% trans 'next' %}</a> {% endif %} <a href="?page={{ paginator.num_pages }}{{ getvars }}" class="btn btn-rounded">{% trans 'last' %}</a> </div> </div> 

Regardless of the GET variables that you have in your request, they will be added after the ?page= GET parameter.

+4
Oct. 20 '14 at 3:56
source share

I had this problem when using django-bootstrap3. The solution (simple) without any template tags uses:

 {% bootstrap_pagination page_obj extra=request.GET.urlencode %} 

It's time to find out ... I finally thanks to this post .

+3
Apr 08 '17 at 19:55 on
source share

Here's a useful custom template tag for building query strings.

 <a href="?{% make_query_string page=obj_list.next_page_number %}">Next page</a> 

If the URL is http://example.com/django/page/?search=sometext , the generated HTML should look something like this:

 <a href="?search=sometext&page=2">Next page</a> 

Other examples:

 <!-- Original URL --> <!-- http://example.com/django/page/?page=1&item=foo&item=bar --> <!-- Add or replace arguments --> {% make_query_string page=2 item="foo2" size=10 %} <!-- Result: page=2&item=foo2&size=10 --> <!-- Append arguments --> {% make_query_string item+="foo2" item+="bar2" %} <!-- Result: page=1&item=foo&item=bar&item=foo2&item=bar2 --> <!-- Remove a specific argument --> {% make_query_string item-="foo" %} <!-- Result: page=1&item=bar --> <!-- Remove all arguments with a specific name --> {% make_query_string item= %} <!-- Result: page=1 --> 

Finally, the source code (written by me):

 # -*- coding: utf-8 -*- from django import template from django.utils.encoding import force_text # Django 1.5+ only register = template.Library() class QueryStringNode(template.Node): def __init__(self, tag_name, parsed_args, var_name=None, silent=False): self.tag_name = tag_name self.parsed_args = parsed_args self.var_name = var_name self.silent = silent def render(self, context): # django.core.context_processors.request should be enabled in # settings.TEMPLATE_CONTEXT_PROCESSORS. # Or else, directly pass the HttpRequest object as 'request' in context. query_dict = context['request'].GET.copy() for op, key, value in self.parsed_args: if op == '+': query_dict.appendlist(key, value.resolve(context)) elif op == '-': list_ = query_dict.getlist(key) value_ = value.resolve(context) try: list_.remove(value_) except ValueError: # Value not found if not isinstance(value_, basestring): # Try to convert it to unicode, and try again try: list_.remove(force_text(value_)) except ValueError: pass elif op == 'd': try: del query_dict[key] except KeyError: pass else: query_dict[key] = value.resolve(context) query_string = query_dict.urlencode() if self.var_name: context[self.var_name] = query_string if self.silent: return '' return query_string @register.tag def make_query_string(parser, token): # {% make_query_string page=1 size= item+="foo" item-="bar" as foo [silent] %} args = token.split_contents() tag_name = args[0] as_form = False if len(args) > 3 and args[-3] == "as": # {% x_make_query_string ... as foo silent %} case. if args[-1] != "silent": raise template.TemplateSyntaxError( "Only 'silent' flag is allowed after %s name, not '%s'." % (tag_name, args[-1])) as_form = True silent = True args = args[:-1] elif len(args) > 2 and args[-2] == "as": # {% x_make_query_string ... as foo %} case. as_form = True silent = False if as_form: var_name = args[-1] raw_pairs = args[1:-2] else: raw_pairs = args[1:] parsed_args = [] for pair in raw_pairs: try: arg, raw_value = pair.split('=', 1) except ValueError: raise template.TemplateSyntaxError( "%r tag argument should be in format foo=bar" % tag_name) operator = arg[-1] if operator == '+': # item+="foo": Append to current query arguments. # eg item=1 -> item=1&item=foo parsed_args.append(('+', arg[:-1], parser.compile_filter(raw_value))) elif operator == '-': # item-="bar": Remove from current query arguments. # eg item=1&item=bar -> item=1 parsed_args.append(('-', arg[:-1], parser.compile_filter(raw_value))) elif raw_value == '': # item=: Completely remove from current query arguments. # eg item=1&item=2 -> '' parsed_args.append(('d', arg, None)) else: # item=1: Replace current query arguments, eg item=2 -> item=1 parsed_args.append(('', arg, parser.compile_filter(raw_value))) if as_form: node = QueryStringNode(tag_name, parsed_args, var_name=var_name, silent=silent) else: node = QueryStringNode(tag_name, parsed_args) return node 
+2
May 7, '13 at 4:06
source share

This is an easy way I do it.

In sight:

 path = '' path += "%s" % "&".join(["%s=%s" % (key, value) for (key, value) in request.GET.items() if not key=='page' ]) 

Then in the template:

 href="?page={{ objects.next_page_number }}&{{path}}" 
+1
Dec 17 '15 at 9:20
source share

Each such link that you put in your opinion should be provided with appropriate parameters. There is no implicit magic that would convert:

http://127.0.0.1:8000/users/?page=2

at

http://127.0.0.1:8000/users/?sort=first_name&page=2

So, you need a Sorter object / class / function / snippet (whatever it is, without overdoing it), which will act similar to django.core.paginator.Paginator, but will handle the sort GET parameter.

It could be that simple:

 sort_order = request.GET.get('sort', 'default-criteria') <paginate, sort> return render_to_response('view.html', { 'paginated_contacts': paginated_contacts, # Paginator stuff 'sort_order': sort_order if sort_oder != 'default-criteria' else '' }) 

Then, in your opinion:

 {% if contacts.has_next %} <a href="?page={{ contacts.next_page_number }}{%if sort_order%}&sort={{sort_oder}}{%endif%}">next</a> {% endif %} 

I could become more general, but I hope you get the concept.

0
Jan 12 '10 at 9:19
source share

'path': request.get_full_path (). rsplit ('& page') [0],

0
Apr 04 '13 at 19:52
source share

I would say, generated the next and previous link from your controller, then passed it to the view and used it from there. I will give you an example (more similar to pseudocode):

 ("next_link", "?param1="+param1+"&param2="+param2+"&page_nr="+(Integer.parseInt(page_nr)-1) 

then in your view use it like this:

 {% if contacts.has_next %} <a href="?page={{ contacts.next_link }}">next</a> {% endif %} 
0
Aug 05 '13 at 10:02
source share

You will need to return a GET as above. You can pass part of the GET request URL by calling

 render_dict['GET'] = request.GET.urlencode(True) return render_to_response('search/search.html', render_dict, context_instance=RequestContext(request)) 

you can use this in a template to create your url, for example.

 href="/search/client/{{ page.no }}/10/?{{ GET }} 
0
Mar 08 '14 at 21:38
source share

With Django Pagination - saving GET parameters is simple.

First, copy the GET parameters into the variable (in view):

 GET_params = request.GET.copy() 

and send it to the template through the context dictionary:

 return render_to_response(template, {'request': request, 'contact': contact, 'GET_params':GET_params}, context_instance=RequestContext(request)) 

The second thing you need to do is use it, specify it in the URLs (href) in the template - an example (extension of the basic html pagination for processing an additional parameter):

 {% if contacts.has_next %} {% if GET_params %} <a href="?{{GET_params.urlencode}}&amp;page={{ contacts.next_page_number }}">next</a> {% else %} <a href="?page={{ contacts.next_page_number }}">next</a> {% endif %} {% endif %} 

Source

0
Mar 17 '16 at 6:52
source share

Improving this by:

Use urlencode from django instead of urllib to prevent a UnicodeEncodeError with unicode arguments.

Template Tag:

 from django.utils.http import urlencode @register.simple_tag(takes_context=True) def url_replace(context, **kwargs): query = context['request'].GET.dict() query.update(kwargs) return urlencode(query) 

Template:

 <!-- Pagination --> <div class="pagination"> <span class="step-links"> {% if coupons.has_previous %} <a href="?{% url_replace page=objects.previous_page_number %}">Prev</a> {% endif %} <span class="current"> Page {{ objects.number }} of {{ objects.paginator.num_pages }} </span> {% if objects.has_next %} <a href="?{% url_replace page=objects.next_page_number %}">Next</a> {% endif %} </span> </div> 
0
Jan 14 '17 at 18:24
source share

Another approach to url_encode solution in this case is simplified with skoval00.

I had a few problems with this version. Firstly, it did not support Unicode encoding, and two, it crashed into filters with several identical keys (for example, the MultipleSelect widget). Because of the .dict () conversion, all but one value is lost. My version supports unicode and several of the same key:

 from django import template from django.utils.html import mark_safe register = template.Library() @register.simple_tag(takes_context=True) def url_replace(context, **kwargs): query = context['request'].GET.copy() for kwarg in kwargs: try: query.pop(kwarg) except KeyError: pass query.update(kwargs) return mark_safe(query.urlencode()) 

This creates a copy of QueryDict and then removes all keys that match kwargs (since an update for QueryDict is added instead of a replacement). Mark_safe was needed due to a double encoding problem.

You would use it like this (don't forget to load tags):

 <a class="next" href="?{% url_replace p=objects.next_page_number%}">Next</a> 

Where? p = 1 is our pagination syntax in the view.

0
Apr 05 '17 at 0:37
source share



All Articles