73π
I thought the custom tags proposed were too complex, this is what I did in the template:
<a href="?{% url_replace request 'page' paginator.next_page_number %}">
And the tag function:
@register.simple_tag
def url_replace(request, field, value):
dict_ = request.GET.copy()
dict_[field] = value
return dict_.urlencode()
If the url_param is not yet in the url, it will be added with value. If it is already there, it will be replaced by the new value. This is a simple solution the suits me, but does not work when the url has multiple parameters with the same name.
You also need the RequestContext request instance to be provided to your template from your view. More info here:
http://lincolnloop.com/blog/2008/may/10/getting-requestcontext-your-templates/
49π
I think url_replace solution may 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.copy()
query.update(kwargs)
return query.urlencode()
with template string simplified to
<a href="?{% url_replace page=paginator.next_page_number %}">
- [Django]-Django β {% csrf_token %} was used in a template, but the context did not provide the value
- [Django]-How to test auto_now_add in django
- [Django]-Why is iterating through a large Django QuerySet consuming massive amounts of memory?
14π
After some playing around I found a solutionβ¦ although I donβt know if itβs really a good one. Iβd prefer a more elegant solution.
Anyway I pass the request to the template and am able to access all the GET variables via request.GET. Then I loop through the GET dictionary and as long as the variable isnβt page I print 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 %}
- [Django]-Update only specific fields in a models.Model
- [Django]-Dynamic choices field in Django Models
- [Django]-How to obtain and/or save the queryset criteria to the DB?
9π
In your views.py
you will somehow access the criteria on which you sort, e.g. first_name
. Youβll need to pass that value to 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 %}
- [Django]-Django admin, hide a model
- [Django]-Aggregate() vs annotate() in Django
- [Django]-Converting Django QuerySet to pandas DataFrame
6π
One can create a context processor to use it wherever pagination is applied.
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 the 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>
Whatever GET variables you have in your request, they will be appended after the ?page=
GET parameter.
- [Django]-Reference list item by index within Django template?
- [Django]-Django Queryset with year(date) = '2010'
- [Django]-Django FileField with upload_to determined at runtime
6π
Improvement of this by:
Use urlencode
from django
instead of urllib
, to prevent UnicodeEncodeError
error 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>
- [Django]-Group by Foreign Key and show related items β Django
- [Django]-How to set up a PostgreSQL database in Django
- [Django]-In Django β Model Inheritance β Does it allow you to override a parent model's attribute?
4π
This is a simple way how I do it
In view :
path = ''
path += "%s" % "&".join(["%s=%s" % (key, value) for (key, value) in request.GET.items() if not key=='page' ])
Then in template:
href="?page={{ objects.next_page_number }}&{{path}}"
- [Django]-How do I return JSON without using a template in Django?
- [Django]-Getting TypeError: __init__() missing 1 required positional argument: 'on_delete' when trying to add parent table after child table with entries
- [Django]-Is it bad to have my virtualenv directory inside my git repository?
4π
I had this problem while using django-bootstrap3. The (easy) solution without any template tags is using:
{% bootstrap_pagination page_obj extra=request.GET.urlencode %}
Took me a while to find this out⦠I finally did thanks to this post.
- [Django]-Create Django model or update if exists
- [Django]-What is the use of PYTHONUNBUFFERED in docker file?
- [Django]-Django get a QuerySet from array of id's in specific order
4π
My solution is based on this one above with the slight improvement to remove &page=
from appearing multiple times. See this comment
@register.simple_tag(takes_context=True)
def url_replace(context, **kwargs):
query = context['request'].GET.copy()
query.pop('page', None)
query.update(kwargs)
return query.urlencode()
This line query.pop('page', None)
silently removes the page from the url
- [Django]-Cross domain at axios
- [Django]-Heroku, postgreSQL, django, comments, tastypie: No operator matches the given name and argument type(s). You might need to add explicit type casts
- [Django]-Create custom buttons in admin change_form in Django
3π
Another take on the url_encode solution, in this case as simplified by skoval00.
I had a few issues with that version. One, it didnβt support Unicode encoding and two, it broke for filters with multiple of the same keys (like a MultipleSelect widget). Due to the .dict() conversion, all values but one are lost. My version supports unicode and multiple 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 QueryDict copy, then removes all keys that match kwargs (since update for a QueryDict adds instead of replacing). Mark_safe was needed due to a double encoding issue.
You would use it like this (donβt forget to load the tags):
<a class="next" href="?{% url_replace p=objects.next_page_number%}">Next</a>
where ?p=1 is our pagination syntax in the View.
- [Django]-Generating a Random Hex Color in Python
- [Django]-Django's forms.Form vs forms.ModelForm
- [Django]-How to disable Django's CSRF validation?
3π
@skoval00 βs answer is the most elegant, however it adds duplicate &page=
query parameters to the url.
Here is the fix:
from urllib.parse import urlencode
from django import template
register = template.Library()
@register.simple_tag(takes_context=True)
def url_replace(context, next_page):
query = context['request'].GET.copy().urlencode()
if '&page=' in query:
url = query.rpartition('&page=')[0]
else:
url = query
return f'{url}&page={next_page}'
- [Django]-Django error when installing Graphite β settings.DATABASES is improperly configured. Please supply the ENGINE value
- [Django]-Django Rest Framework β Could not resolve URL for hyperlinked relationship using view name "user-detail"
- [Django]-How to convert a Django QuerySet to a list?
2π
Hereβs a useful custom template tag for constructing 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 be something like:
<a href="?search=sometext&page=2">Next page</a>
More 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'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's argument should be in format foo=bar" % tag_name)
operator = arg[-1]
if operator == '+':
# item+="foo": Append to current query arguments.
# e.g. 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.
# e.g. 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.
# e.g. item=1&item=2 -> ''
parsed_args.append(('d', arg, None))
else:
# item=1: Replace current query arguments, e.g. 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
- [Django]-New url format in Django 1.9
- [Django]-Django: TemplateDoesNotExist (rest_framework/api.html)
- [Django]-Write only, read only fields in django rest framework
2π
Another slight modification to skoval00 and Reinstate Monica to fully get rid of duplication and avoid the ugly ?&page=1
part:
from urllib.parse import urlencode
from django import template
register = template.Library()
@register.simple_tag(takes_context=True)
def url_replace(context, next_page):
if query.startswith('page') or not len(query):
new_url = f'page={next_page}'
elif '&page=' in query:
get_params = query.rpartition('&page=')[0] # equivalent to .split('page='), except more efficient
new_url = f'{get_params}&page={next_page}'
else:
new_url = f'{query}&page={next_page}'
return new_url
- [Django]-RuntimeWarning: DateTimeField received a naive datetime
- [Django]-How do I migrate a model out of one django app and into a new one?
- [Django]-Python + Django page redirect
1π
@Elrond Supports Monica
@register.simple_tag(takes_context=True)
def url_replace(context, **kwargs):
query = context['request'].GET.copy()
for key in kwargs:
query[key] = kwargs[key]
return query.urlencode()
Use in template
<a class="page-link" href="?{% url_replace p=1 q='bar'%}">
- [Django]-Using a UUID as a primary key in Django models (generic relations impact)
- [Django]-Django upgrading to 1.9 error "AppRegistryNotReady: Apps aren't loaded yet."
- [Django]-Django The 'image' attribute has no file associated with it
0π
Every such link you put in your view has to be equipped with relevant parameters. There is no implicit magic that would convert:
http://127.0.0.1:8000/users/?page=2
into:
http://127.0.0.1:8000/users/?sort=first_name&page=2
So what you need is some Sorter
object/class/function/snippet (whatever might fit here without overdoing it), that would act similarly to django.core.paginator.Paginator, but would handle sort
GET parameter.
It could be as simple as this:
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 view:
{% if contacts.has_next %}
<a href="?page={{ contacts.next_page_number }}{%if sort_order%}&sort={{sort_oder}}{%endif%}">next</a>
{% endif %}
I could be made more generic, but I hope you get the concept.
- [Django]-Delete multiple objects in django
- [Django]-How to check if ManyToMany field is not empty?
- [Django]-Django storages: Import Error β no module named storages
0π
I would say generate the next and previous link from your controller, then pass it to the view and use it from there. I will give you an example (more like a pseudocode):
("next_link", "?param1="+param1+"¶m2="+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 %}
- [Django]-Assign variables to child template in {% include %} tag Django
- [Django]-How to get the current URL within a Django template?
- [Django]-Django CSRF check failing with an Ajax POST request
0π
You will need to return the GET as stated above. You can pass the GET request part of the 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 then use this in the template to build your URL e.g.
href="/search/client/{{ page.no }}/10/?{{ GET }}
- [Django]-How to resize the new uploaded images using PIL before saving?
- [Django]-Checking for empty queryset in Django
- [Django]-Problems extend change_form.html in django admin
0π
With Djangoβs Pagination β preserving the GET params is simple.
First copy the GET params to a variable (in view):
GET_params = request.GET.copy()
and send it to the template in via context dictionary:
return render_to_response(template,
{'request': request, 'contact': contact, 'GET_params':GET_params}, context_instance=RequestContext(request))
Second thing you need to do is use it, specify it in the url calls (href) in the template β an example (extending the basic pagination html to handle extra param condition):
{% if contacts.has_next %}
{% if GET_params %}
<a href="?{{GET_params.urlencode}}&page={{ contacts.next_page_number }}">next</a>
{% else %}
<a href="?page={{ contacts.next_page_number }}">next</a>
{% endif %}
{% endif %}
- [Django]-How to use subquery in django?
- [Django]-How to reset migrations in Django 1.7
- [Django]-Django: Record with max element
0π
your code should be like:
{% 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 %}
- [Django]-Backwards migration with Django South
- [Django]-Django + Ajax
- [Django]-How to convert a Django QuerySet to a list?
0π
this works for me and i find it simpler
from django.http import HttpRequest
def get_query_params_href(req: HttpRequest):
query_strings = req.GET.dict()
string = '?'
for i in query_strings:
string += f'{i}={query_strings[i]}&'
return string[0:string.__len__()-1]
- [Django]-Why is assertDictEqual needed if dicts can be compared by `==`?
- [Django]-How can I serialize a queryset from an unrelated model as a nested serializer?
- [Django]-Why there are two process when i run python manage.py runserver
- [Django]-Can "list_display" in a Django ModelAdmin display attributes of ForeignKey fields?
- [Django]-Django equivalent for count and group by
- [Django]-Simple Log to File example for django 1.3+