Как разбивать Django на другие переменные get?
У меня возникают проблемы с использованием разбивки на страницы в Django. В качестве примера возьмите URL-адрес ниже:
http://127.0.0.1:8000/users/?sort=first_name
На этой странице я сортирую список пользователей по их первому имени. Без переменной типа GET он по умолчанию сортирует по id.
Теперь, если я нажму следующую ссылку, я ожидаю следующий URL:
http://127.0.0.1:8000/users/?sort=first_name&page=2
Вместо этого я теряю все переменные get и заканчиваю
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, и я предпочитаю продолжать использовать его. Код шаблона, который я использую, аналогичен этому:
{% if contacts.has_next %}
<a href="?page={{ contacts.next_page_number }}">next</a>
{% endif %}
Ответы
Ответ 1
Я думал, что предлагаемые теги были слишком сложными, вот что я сделал в шаблоне:
<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-адресе, он будет добавлен со значением. Если он уже существует, он будет заменен новым значением. Это простое решение, которое мне подходит, но не работает, когда URL имеет несколько параметров с тем же именем.
Вам также нужно, чтобы экземпляр запроса RequestContext был предоставлен вашему шаблону из вашего представления. Подробнее здесь:
http://lincolnloop.com/blog/2008/may/10/getting-requestcontext-your-templates/
Ответ 2
Я думаю, что решение url_replace может быть переписано более элегантно, поскольку
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)
с строкой шаблона, упрощенной до
<a href="?{% url_replace page=paginator.next_page_number %}">
Это позволяет заменить несколько клавиш, но не позволяет выполнять запросы с помощью повторяющихся ключей.
Ответ 3
После некоторых игр я нашел решение... хотя я не знаю, действительно ли он хороший. Я бы предпочел более элегантное решение.
В любом случае я передаю запрос шаблону и могу получить доступ ко всем переменным GET через request.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 %}
Ответ 4
В вашем views.py
вы каким-то образом получите доступ к критериям, которые вы сортируете, например. first_name
. Вам нужно будет передать это значение в шаблон и вставить его там, чтобы его запомнить.
Пример:
{% if contacts.has_next %}
<a href="?sort={{ criteria }}&page={{ contacts.next_page_number }}">next</a>
{% endif %}
Ответ 5
Можно создать процессор контекста, чтобы использовать его везде, где применяется разбиение на страницы.
Например, в 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.
Ответ 6
У меня была эта проблема при использовании django-bootstrap3. Решение (простое) без каких-либо шаблонных тегов использует:
{% bootstrap_pagination page_obj extra=request.GET.urlencode %}
Пришло время узнать это... Я, наконец, благодаря этот пост.
Ответ 7
Здесь полезный настраиваемый тег шаблона для построения строк запроса.
<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 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.
# 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
Это простой способ, как я это делаю
В поле зрения:
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}}"
Ответ 9
Каждая такая ссылка, которую вы положите на ваш взгляд, должна быть снабжена соответствующими параметрами. Нет неявной магии, которая бы конвертировала:
http://127.0.0.1:8000/users/?page=2
в
http://127.0.0.1:8000/users/?sort=first_name&page=2
Итак, вам нужен Sorter
object/class/function/snippet (что бы там ни было, не переусердствуя), что будет действовать аналогично 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 %}
Я мог бы стать более общим, но я надеюсь, что вы получите концепцию.
Ответ 10
'path': request.get_full_path(). rsplit ('& page') [0],
Ответ 11
Я бы сказал, сгенерировал следующую и предыдущую ссылку с вашего контроллера, затем передал ее в представление и использовал его оттуда. Я приведу вам пример (более похожий на псевдокод):
("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 %}
Ответ 12
Вам нужно будет вернуть GET, как указано выше. Вы можете передать часть запроса GET URL-адреса, вызвав
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 }}
Ответ 13
С Django Pagination - сохранение параметров 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 разбиения на страницы для обработки дополнительного параметра):
{% 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 %}
Источник
Ответ 14
Улучшение этого посредством:
Используйте 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>
Ответ 15
Другой подход к решению url_encode в этом случае упрощен с помощью skoval00.
У меня было несколько проблем с этой версией. Во-первых, он не поддерживал кодировку Unicode и два, он разбился на фильтры с несколькими одинаковыми ключами (например, виджет MultipleSelect). Из-за преобразования .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 - наш синтаксис разбиения на страницы в представлении.