Ich habe Probleme mit der Paginierung in Django . Nehmen Sie die folgende URL als Beispiel:
http://127.0.0.1:8000/users/?sort=first_name
Auf dieser Seite sortiere ich eine Liste der Benutzer nach ihrem Vornamen. Ohne eine sort GET-Variable wird standardmäßig nach id sortiert.
Wenn ich jetzt auf den nächsten Link klicke, erwarte ich die folgende URL:
http://127.0.0.1:8000/users/?sort=first_name&page=2
Stattdessen verliere ich alle get Variablen und ende mit
http://127.0.0.1:8000/users/?page=2
Dies ist ein Problem, da die zweite Seite nach ID anstelle von Vorname sortiert ist.
Wenn ich request.get_full_path verwende, erhalte ich schließlich eine hässliche URL:
http://127.0.0.1:8000/users/?sort=first_name&page=2&page=3&page=4
Was ist die Lösung? Gibt es eine Möglichkeit, auf die GET-Variablen in der Vorlage zuzugreifen und den Wert für die Seite zu ersetzen?
Ich verwende die Paginierung wie in der Dokumentation von Django beschrieben und bevorzuge es, sie weiterhin zu verwenden. Der von mir verwendete Vorlagencode ähnelt dem folgenden:
{% if contacts.has_next %}
<a href="?page={{ contacts.next_page_number }}">next</a>
{% endif %}
quelle
request
als Parameter übergeben zu müssen. Siehe diese Antwort stackoverflow.com/a/2160298/1272513Ich denke, dass die url_replace- Lösung eleganter umgeschrieben werden kann als
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()
mit Vorlagenstring vereinfacht zu
<a href="?{% url_replace page=paginator.next_page_number %}">
quelle
urllib.parse.urlencode()
. Siehe diese Frage .import urllib
undreturn urllib.urlencode(query)
.django.template.context_processors.request
Kontextprozessor aktiviert sein. Obwohl es standardmäßig aktiviert ist.query = context['request'].GET.copy()
undreturn query.urlencode()
&p=2&p=3&p=4
Nach einigem Herumspielen habe ich eine Lösung gefunden ... obwohl ich nicht weiß, ob es wirklich eine gute ist. Ich würde eine elegantere Lösung bevorzugen.
Auf jeden Fall übergebe ich die Anfrage an die Vorlage und kann über request.GET auf alle GET-Variablen zugreifen. Dann durchlaufe ich das GET-Wörterbuch und drucke es, solange die Variable keine Seite ist.
{% 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 %}
quelle
request
als Parameter verwendet wird, und dann Ihre Parameterzeichenfolge zurück in die Vorlage drucken.In Ihrem werden
views.py
Sie irgendwie auf die Kriterien zugreifen, nach denen Sie sortieren, zfirst_name
. Sie müssen diesen Wert an die Vorlage übergeben und dort einfügen, um sich daran zu erinnern.Beispiel:
{% if contacts.has_next %} <a href="?sort={{ criteria }}&page={{ contacts.next_page_number }}">next</a> {% endif %}
quelle
Man kann einen Kontextprozessor erstellen, um ihn überall dort zu verwenden, wo Paginierung angewendet wird.
Zum Beispiel 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())}
Fügen Sie den Kontextprozessor zu Ihren Django-Projekteinstellungen hinzu:
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', )
In Ihren Vorlagen können Sie dies dann beim Paginieren verwenden:
<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>
Unabhängig davon, welche GET-Variablen Sie in Ihrer Anfrage haben, werden sie nach dem
?page=
GET-Parameter angehängt .quelle
Verbesserung der dieser durch:
Verwenden Sie
urlencode
vondjango
anstelle vonurllib
, umUnicodeEncodeError
Fehler mitunicode
Argumenten zu vermeiden .Vorlagen-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)
Vorlage:
<!-- 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>
quelle
Ich hatte dieses Problem bei der Verwendung von django-bootstrap3. Die (einfache) Lösung ohne Vorlagen-Tags verwendet:
Ich habe eine Weile gebraucht, um das herauszufinden ... Ich habe es endlich dank dieses Beitrags getan .
quelle
Hier ist ein nützliches benutzerdefiniertes Vorlagen-Tag zum Erstellen von Abfragezeichenfolgen.
<a href="?{% make_query_string page=obj_list.next_page_number %}">Next page</a>
Wenn die URL http://example.com/django/page/?search=sometext lautet , sollte der generierte HTML-Code wie folgt lauten:
<a href="?search=sometext&page=2">Next page</a>
Mehr Beispiele:
<!-- 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 -->
Zum Schluss der Quellcode (von mir geschrieben):
# -*- 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
quelle
@ skoval00 ‚s Antwort ist die am meisten elegant, es fügt jedoch doppelte
&page=
Abfrage - Parameter an die URL.Hier ist das Update:
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] # equivalent to .split('page='), except more efficient else: url = query return f'{url}&page={next_page}'
quelle
Meine Lösung ist auf der Grundlage dieser oben mit der leichten Verbesserung eines entfernen
&page=
erscheint mehrmals. Siehe diesen Kommentar@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()
Diese Zeile
query.pop('page', None)
entfernt die Seite stillschweigend aus der URLquelle
Dies ist ein einfacher Weg, wie ich es mache
Im Hinblick auf :
path = '' path += "%s" % "&".join(["%s=%s" % (key, value) for (key, value) in request.GET.items() if not key=='page' ])
Dann in Vorlage:
href="?page={{ objects.next_page_number }}&{{path}}"
quelle
Eine weitere Variante der url_encode-Lösung, in diesem Fall vereinfacht durch skoval00.
Ich hatte einige Probleme mit dieser Version. Erstens wurde die Unicode-Codierung nicht unterstützt, und zweitens wurden Filter mit mehreren gleichen Schlüsseln (wie bei einem MultipleSelect-Widget) nicht verwendet. Durch die Konvertierung von .dict () gehen alle Werte bis auf einen verloren. Meine Version unterstützt Unicode und mehrere gleiche Schlüssel:
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())
Dadurch wird eine QueryDict-Kopie erstellt und anschließend alle Schlüssel entfernt, die mit kwargs übereinstimmen (da das Update für ein QueryDict hinzugefügt wird, anstatt es zu ersetzen). Mark_safe wurde aufgrund eines Problems mit der doppelten Codierung benötigt.
Sie würden es so verwenden (vergessen Sie nicht, die Tags zu laden):
<a class="next" href="?{% url_replace p=objects.next_page_number%}">Next</a>
Dabei ist? p = 1 unsere Paginierungssyntax in der Ansicht.
quelle
{% include "core/pagination.html" with objects=ads_list %}
einfügen, in die Sie paginieren möchten: Objekte ist der generische Name dessen, was Sie für die allgemeine Vorlage paginieren, und Sie können ihr den Namen zuweisen, der in dieser bestimmten Vorlage (in diesem Fall ads_list) aufgerufen wird.@ Elrond unterstützt 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()
In Vorlage verwenden
<a class="page-link" href="?{% url_replace p=1 q='bar'%}">
quelle
Jeder solche Link, den Sie in Ihre Ansicht einfügen, muss mit relevanten Parametern ausgestattet sein. Es gibt keine implizite Magie, die konvertieren würde:
http://127.0.0.1:8000/users/?page=2
in:
http://127.0.0.1:8000/users/?sort=first_name&page=2
Was Sie also brauchen, ist ein
Sorter
Objekt / eine Klasse / eine Funktion / ein Snippet (was auch immer hier passen könnte, ohne es zu übertreiben), das sich ähnlich wie django.core.paginator.Paginator verhält, aber densort
GET-Parameter verarbeitet.Es könnte so einfach sein:
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 '' })
Dann aus Ihrer Sicht:
{% if contacts.has_next %} <a href="?page={{ contacts.next_page_number }}{%if sort_order%}&sort={{sort_oder}}{%endif%}">next</a> {% endif %}
Ich könnte allgemeiner gemacht werden, aber ich hoffe, Sie bekommen das Konzept.
quelle
Ich würde sagen, generieren Sie den nächsten und vorherigen Link von Ihrem Controller, übergeben Sie ihn dann an die Ansicht und verwenden Sie ihn von dort aus. Ich werde Ihnen ein Beispiel geben (eher wie ein Pseudocode):
("next_link", "?param1="+param1+"¶m2="+param2+"&page_nr="+(Integer.parseInt(page_nr)-1)
Verwenden Sie es dann aus Ihrer Sicht folgendermaßen:
{% if contacts.has_next %} <a href="?page={{ contacts.next_link }}">next</a> {% endif %}
quelle
Sie müssen das GET wie oben angegeben zurücksenden. Sie können den GET-Anforderungsteil der URL durch Aufrufen übergeben
render_dict['GET'] = request.GET.urlencode(True) return render_to_response('search/search.html', render_dict, context_instance=RequestContext(request))
Sie können dies dann in der Vorlage verwenden, um Ihre URL zu erstellen, z
href="/search/client/{{ page.no }}/10/?{{ GET }}
quelle
Mit Djangos Paginierung ist es einfach, die GET-Parameter beizubehalten.
Kopieren Sie zuerst die GET-Parameter in eine Variable (in Ansicht):
GET_params = request.GET.copy()
und senden Sie es über das Kontextwörterbuch an die Vorlage:
return render_to_response(template, {'request': request, 'contact': contact, 'GET_params':GET_params}, context_instance=RequestContext(request))
Das zweite, was Sie tun müssen, ist es zu verwenden, es in den URL-Aufrufen (href) in der Vorlage anzugeben - ein Beispiel (Erweiterung des grundlegenden Paginierungs-HTML, um zusätzliche Parameterbedingungen zu behandeln):
{% 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 %}
Quelle
quelle
Eine weitere geringfügige Änderung an skoval00 und Reinstate Monica , um Doppelarbeit vollständig zu beseitigen und den hässlichen
?&page=1
Teil zu vermeiden :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
quelle
'path': request.get_full_path (). rsplit ('& page') [0],
quelle