Django를 다른 get 변수로 페이지 매김하는 방법은 무엇입니까?
Django 에서 페이지 매김을 사용하는 데 문제가 있습니다 . 아래 URL을 예로 들어 보겠습니다.
http://127.0.0.1:8000/users/?sort=first_name
이 페이지에서는 사용자 목록을 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
두 번째 페이지가 first_name 대신 id로 정렬되기 때문에 이는 문제입니다.
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 %}
제안 된 사용자 정의 태그가 너무 복잡하다고 생각했습니다. 이것이 템플릿에서 수행 한 작업입니다.
<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/
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.copy()
query.update(kwargs)
return query.urlencode()
단순화 된 템플릿 문자열 사용
<a href="?{% url_replace page=paginator.next_page_number %}">
몇 번 놀아 본 후 해결책을 찾았습니다. 정말 좋은 것인지는 모르겠습니다. 더 우아한 솔루션을 선호합니다.
어쨌든 요청을 템플릿에 전달하고 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 %}
당신의에서 views.py당신은 어떻게 든 예를 들어, 어떤에서 당신 분류 기준에 액세스합니다 first_name. 해당 값을 템플릿에 전달하고이를 기억하기 위해 여기에 삽입해야합니다.
예:
{% if contacts.has_next %}
<a href="?sort={{ criteria }}&page={{ contacts.next_page_number }}">next</a>
{% endif %}
페이지 매김이 적용될 때마다 사용할 컨텍스트 프로세서를 만들 수 있습니다.
예를 들어 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 변수가 무엇이든 GET ?page=매개 변수 뒤에 추가됩니다 .
django-bootstrap3을 사용하는 동안이 문제가 발생했습니다. 템플릿 태그가없는 (쉬운) 솔루션은 다음을 사용합니다.
{% bootstrap_pagination page_obj extra=request.GET.urlencode %}
이걸 알아내는 데 시간이 좀 걸렸습니다 ... 마침내이 게시물 덕분에 했습니다 .
다음은 쿼리 문자열을 구성하는 데 유용한 사용자 지정 템플릿 태그입니다.
<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.
# 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
의 개선 이 의 :
인수 오류 를 방지 하려면 대신 urlencodefrom django을 사용하십시오 .urllibUnicodeEncodeErrorunicode
템플릿 태그 :
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>
이것은 내가 그것을하는 간단한 방법입니다
보기 :
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
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.
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 %}
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 }}
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 %}
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.
@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] # equivalent to .split('page='), except more efficient
else:
url = query
return f'{url}&page={next_page}'
'path': request.get_full_path().rsplit('&page')[0],
참고URL : https://stackoverflow.com/questions/2047622/how-to-paginate-django-with-other-get-variables
'IT TIP' 카테고리의 다른 글
| 64 비트 Windows에 SciPy를 어떻게 설치합니까? (0) | 2020.11.20 |
|---|---|
| C #을 네이티브로 컴파일 하시겠습니까? (0) | 2020.11.20 |
| git bash Windows 바로 가기를 수정할 수 없습니다. (0) | 2020.11.20 |
| GIT_WORK_TREE는 무엇입니까?이 ENV 변수를 설정할 필요가없는 이유는 무엇입니까? (0) | 2020.11.20 |
| 파이썬 JSON은 첫 번째 수준에서만 키를 얻습니다. (0) | 2020.11.20 |