我在Django中使用分页时遇到问题。以下面的URL为例:
http://127.0.0.1:8000/users/?sort=first_name
在此页面上,我按其first_name对用户列表进行排序。没有排序GET变量,它默认按id排序。
现在,如果我点击下一个链接,我希望以下网址:
http://127.0.0.1:8000/users/?sort=first_name&page=2
相反,我失去了所有获取变量并以
结束http://127.0.0.1:8000/users/?page=2
这是一个问题,因为第二页按id而不是first_name排序。
如果我使用request.get_full_path,我最终会得到一个丑陋的URL:
http://127.0.0.1:8000/users/?sort=first_name&page=2&page=3&page=4
解决方案是什么?有没有办法访问模板上的GET变量并替换页面的值?
我正在使用Django's documentation中描述的分页,我的偏好是继续使用它。我使用的模板代码与此类似:
{% if contacts.has_next %}
<a href="?page={{ contacts.next_page_number }}">next</a>
{% endif %}
答案 0 :(得分:52)
我认为提出的自定义标签过于复杂,这就是我在模板中所做的:
<a href="?{% url_replace request 'page' paginator.next_page_number %}">
标签功能:
@register.simple_tag
def url_replace(request, field, value):
dict_ = request.GET.copy()
dict_[field] = value
return dict_.urlencode()
如果url_param尚未在网址中,则会添加值。如果它已经存在,它将被新值替换。这是一个适合我的简单解决方案,但是当url具有多个具有相同名称的参数时不起作用。
您还需要从视图中为您的模板提供RequestContext请求实例。更多信息:
http://lincolnloop.com/blog/2008/may/10/getting-requestcontext-your-templates/
答案 1 :(得分:31)
我认为 url_replace 解决方案可以更优雅地重写为
async await
将模板字符串简化为
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()
答案 2 :(得分:11)
经过一番游戏,我找到了一个解决方案......虽然我不知道它是不是真的很好。我更喜欢更优雅的解决方案。
无论如何,我将请求传递给模板,并且能够通过request.GET访问所有GET变量。然后我遍历GET字典,只要变量不是页面,我就打印它。
{% 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 %}
答案 3 :(得分:6)
在views.py
中,您将以某种方式访问您对其进行排序的条件,例如first_name
。您需要将该值传递给模板并将其插入其中以便记住它。
示例:
{% if contacts.has_next %}
<a href="?sort={{ criteria }}&page={{ contacts.next_page_number }}">next</a>
{% endif %}
答案 4 :(得分:4)
可以创建一个上下文处理器,以便在应用分页的任何地方使用它。
例如,在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())}
将上下文处理器添加到Django项目设置中:
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',
)
然后,在模板中,您可以在分页时使用它:
<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>
无论您的请求中包含哪些GET变量,它们都会附加在?page=
GET参数之后。
答案 5 :(得分:4)
使用django-bootstrap3时出现此问题。没有任何模板标签的(简单)解决方案正在使用:
{% bootstrap_pagination page_obj extra=request.GET.urlencode %}
我花了一段时间才发现这一点......我终于感谢this post。
答案 6 :(得分:3)
我的解决方案基于上面的this,略有改进,可以将&page=
删除多次。看到这个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()
此行query.pop('page', None)
会将网页从url中静默删除
答案 7 :(得分:2)
这是一个有用的自定义模板标记,用于构造查询字符串。
<a href="?{% make_query_string page=obj_list.next_page_number %}">Next page</a>
如果网址为http://example.com/django/page/?search=sometext,则生成的HTML应为:
<a href="?search=sometext&page=2">Next page</a>
更多例子:
<!-- 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 -->
最后,源代码(由我编写):
# -*- 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
答案 8 :(得分:2)
this的改进:
使用urlencode
而不是django
中的urllib
来防止UnicodeEncodeError
个unicode
参数出错。
模板标签:
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)
模板:
<!-- 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>
答案 9 :(得分:1)
@Elrond支持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()
在模板中使用
<a class="page-link" href="?{% url_replace p=1 q='bar'%}">
答案 10 :(得分:1)
这是我怎么做的简单方法
在视图中:
path = ''
path += "%s" % "&".join(["%s=%s" % (key, value) for (key, value) in request.GET.items() if not key=='page' ])
然后在模板中:
href="?page={{ objects.next_page_number }}&{{path}}"
答案 11 :(得分:0)
你的代码应该是这样的:
{% 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 %}
答案 12 :(得分:0)
对 skoval00 和恢复莫妮卡的另一项轻微修改,以完全消除重复并避免使用丑陋的?&page=1
部分:
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
答案 13 :(得分:0)
@ skoval00的答案是 most 优雅的,但是它向网址中添加了重复的import React from 'react';
import './Feed.scss';
const feed = (props) => {
return(
<div className="parent">
<img src={props.src} className="img-fluid" alt=""/>
</div>
);
};
export default feed;
查询参数。
这里是解决方法:
&page=
答案 14 :(得分:0)
对url_encode解决方案的另一种看法,在本例中由skoval00简化。
我对该版本有一些问题。一,它不支持Unicode编码和两个,它打破了具有多个相同键的过滤器(如MultiSelect小部件)。由于.dict()转换,所有值都会丢失。我的版本支持unicode和多个相同的密钥:
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())
这会创建一个QueryDict副本,然后删除所有与kwargs匹配的键(因为QueryDict的更新会添加而不是替换)。由于双重编码问题,需要Mark_safe。
你会像这样使用它(不要忘记加载标签):
<a class="next" href="?{% url_replace p=objects.next_page_number%}">Next</a>
其中?p = 1是我们在视图中的分页语法。
答案 15 :(得分:0)
使用Django的分页 - 保留GET参数很简单。
首先将GET参数复制到变量(视图中):
GET_params = request.GET.copy()
并通过上下文字典将其发送到模板:
return render_to_response(template,
{'request': request, 'contact': contact, 'GET_params':GET_params}, context_instance=RequestContext(request))
你需要做的第二件事是使用它,在模板中的url调用(href)中指定它 - 一个例子(扩展基本的分页html以处理额外的param条件):
{% 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 %}
答案 16 :(得分:0)
如上所述,您需要返回GET。您可以通过调用
传递URL的GET请求部分render_dict['GET'] = request.GET.urlencode(True)
return render_to_response('search/search.html',
render_dict,
context_instance=RequestContext(request))
然后,您可以在模板中使用它来构建您的URL,例如
href="/search/client/{{ page.no }}/10/?{{ GET }}
答案 17 :(得分:0)
我想说从控制器生成下一个和上一个链接,然后将其传递给视图并从那里使用它。我会给你一个例子(更像是伪代码):
("next_link", "?param1="+param1+"¶m2="+param2+"&page_nr="+(Integer.parseInt(page_nr)-1)
然后在您的视图中使用它:
{% if contacts.has_next %}
<a href="?page={{ contacts.next_link }}">next</a>
{% endif %}
答案 18 :(得分:0)
您在视图中添加的每个此类链接都必须配备相关参数。没有隐含的魔法可以转换:
http://127.0.0.1:8000/users/?page=2
成:
http://127.0.0.1:8000/users/?sort=first_name&page=2
所以你需要的是一些Sorter
对象/类/函数/片段(无论什么可能适合这里而不过度使用它),它的行为类似于django.core.paginator.Paginator,但会处理{{1 GET参数。
可能就是这么简单:
sort
然后,在您看来:
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 ''
})
我可以变得更通用,但我希望你能得到这个概念。
答案 19 :(得分:-1)
'path':request.get_full_path()。rsplit('&amp; page')[0],