如何分页Django与其他获取variables?
我在Django中使用分页时遇到问题。 以下面的URL为例:
http://127.0.0.1:8000/users/?sort=first_name
在这个页面上,我按他们的first_name对用户列表进行sorting。 没有一个sorting的GETvariables,它默认按idsorting。
现在,如果我点击下一个链接,我期望以下url:
http://127.0.0.1:8000/users/?sort=first_name&page=2
相反,我失去了所有的variables,并最终
http://127.0.0.1:8000/users/?page=2
这是一个问题,因为第二个页面是由ID而不是first_namesorting。
如果我使用request.get_full_path,我最终会得到一个丑陋的URL:
http://127.0.0.1:8000/users/?sort=first_name&page=2&page=3&page=4
解决办法是什么? 有没有办法访问模板上的GETvariables并replace页面的值?
我正在使用Django的文档中描述的分页,我的首选是继续使用它。 我使用的模板代码与此类似:
{% if contacts.has_next %} <a href="?page={{ contacts.next_page_number }}">next</a> {% endif %}
我认为提出的定制标签太复杂了,这就是我在模板中所做的:
<a href="?{% url_replace request 'page' paginator.next_page_number %}">
而标签function:
@register.simple_tag def url_replace(request, field, value): dict_ = request.GET.copy() dict_[field] = value return dict_.urlencode()
如果url_param尚未在url中,则会添加值。 如果它已经在那里,它将被新值所取代。 这是一个简单的解决scheme适合我,但不具有同名的多个参数。
您还需要将RequestContext请求实例从您的视图提供给您的模板。 更多信息:
http://lincolnloop.com/blog/2008/may/10/getting-requestcontext-your-templates/
我认为url_replace解决scheme可能会被更好地重写为
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)
模板string简化为
<a href="?{% url_replace page=paginator.next_page_number %}">
这允许replace多个键,但不允许具有重复键的查询。
在玩了一段时间之后,我发现了一个解决scheme…虽然我不知道它是否真的很好。 我更喜欢更优雅的解决scheme。
无论如何,我将请求传递给模板,并能够通过request.GET访问所有的GETvariables。 然后我通过GET字典循环,只要variables不是页面我打印它。
{% 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 %}
在你的views.py
你会以某种方式访问你sorting的标准,例如first_name
。 您需要将该值传递给模板并将其插入到该模板中以记住它。
例:
{% if contacts.has_next %} <a href="?sort={{ criteria }}&page={{ contacts.next_page_number }}">next</a> {% endif %}
人们可以创build一个上下文处理器来在任何分页应用的地方使用它。
例如,在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>
无论您的请求中有?page=
GETvariables,它们都会被添加到?page=
GET参数之后。
我在使用django-bootstrap3时遇到了这个问题。 没有任何模板标签的(简单)解决scheme正在使用:
{% bootstrap_pagination page_obj extra=request.GET.urlencode %}
花了我一会儿才发现这一点…我终于感谢这个职位 。
这是构build查询string的一个有用的自定义模板标签。
<a href="?{% make_query_string page=obj_list.next_page_number %}">Next page</a>
如果URL是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. # 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
这是一个简单的方法,我怎么做
鉴于:
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}}"
你所看到的每一个这样的链接都必须配备相关的参数。 没有隐含的魔法可以转换:
http://127.0.0.1:8000/users/?page=2
成:
http://127.0.0.1:8000/users/?sort=first_name&page=2
所以你需要的是一些Sorter
对象/类/函数/代码片段(不pipe过度的话可能适合这里),这将类似于django.core.paginator.Paginator,但将处理sort
GET参数。
它可以像这样简单:
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 '' })
那么,在你看来:
{% if contacts.has_next %} <a href="?page={{ contacts.next_page_number }}{%if sort_order%}&sort={{sort_oder}}{%endif%}">next</a> {% endif %}
我可以变得更通用,但是我希望你能理解这个概念。
'path':request.get_full_path()。rsplit('&page')[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 %}
您将需要如上所述返回GET。 您可以通过调用来传递url的GET请求部分
render_dict['GET'] = request.GET.urlencode(True) return render_to_response('search/search.html', render_dict, context_instance=RequestContext(request))
然后你可以在模板中使用这个来构build你的URL,例如
href="/search/client/{{ page.no }}/10/?{{ GET }}
使用Django的分页 – 保留GET参数很简单。
首先将GET PARAMS复制到一个variables(在视图中):
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来处理额外的参数条件):
{% 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
而不是urllib
urlencode
来防止unicode
参数的UnicodeEncodeError
错误。
模板标签:
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>
另一个采取url_encode解决scheme,在这种情况下由skoval00简化。
那个版本我有几个问题。 一个,它不支持Unicode编码和两个,它打破了与多个相同的密钥(如MultipleSelect部件)的filter。 由于.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())
这将创build一个QueryDict副本,然后删除与kwargs匹配的所有键(因为QueryDict的更新添加而不是replace)。 由于双重编码问题,需要Mark_safe。
你会像这样使用它(不要忘记加载标签):
<a class="next" href="?{% url_replace p=objects.next_page_number%}">Next</a>
其中?p = 1是View中的分页语法。