Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
D
django-rest-framework
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
django-rest-framework
Commits
3833a5bb
Commit
3833a5bb
authored
Jan 14, 2015
by
Tom Christie
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Include pagination control in browsable API
parent
f13fcba9
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
143 additions
and
5 deletions
+143
-5
rest_framework/pagination.py
+89
-1
rest_framework/renderers.py
+1
-0
rest_framework/static/rest_framework/css/bootstrap-tweaks.css
+0
-4
rest_framework/templates/rest_framework/base.html
+9
-0
rest_framework/templates/rest_framework/pagination/numbers.html
+27
-0
rest_framework/templatetags/rest_framework.py
+17
-0
No files found.
rest_framework/pagination.py
View file @
3833a5bb
...
@@ -3,14 +3,18 @@ Pagination serializers determine the structure of the output that should
...
@@ -3,14 +3,18 @@ Pagination serializers determine the structure of the output that should
be used for paginated responses.
be used for paginated responses.
"""
"""
from
__future__
import
unicode_literals
from
__future__
import
unicode_literals
from
collections
import
namedtuple
from
django.core.paginator
import
InvalidPage
,
Paginator
as
DjangoPaginator
from
django.core.paginator
import
InvalidPage
,
Paginator
as
DjangoPaginator
from
django.template
import
Context
,
loader
from
django.utils
import
six
from
django.utils
import
six
from
django.utils.translation
import
ugettext
as
_
from
django.utils.translation
import
ugettext
as
_
from
rest_framework.compat
import
OrderedDict
from
rest_framework.compat
import
OrderedDict
from
rest_framework.exceptions
import
NotFound
from
rest_framework.exceptions
import
NotFound
from
rest_framework.response
import
Response
from
rest_framework.response
import
Response
from
rest_framework.settings
import
api_settings
from
rest_framework.settings
import
api_settings
from
rest_framework.templatetags.rest_framework
import
replace_query_param
from
rest_framework.templatetags.rest_framework
import
(
replace_query_param
,
remove_query_param
)
def
_strict_positive_int
(
integer_string
,
cutoff
=
None
):
def
_strict_positive_int
(
integer_string
,
cutoff
=
None
):
...
@@ -35,6 +39,49 @@ def _get_count(queryset):
...
@@ -35,6 +39,49 @@ def _get_count(queryset):
return
len
(
queryset
)
return
len
(
queryset
)
def
_get_displayed_page_numbers
(
current
,
final
):
"""
This utility function determines a list of page numbers to display.
This gives us a nice contextually relevant set of page numbers.
For example:
current=14, final=16 -> [1, None, 13, 14, 15, 16]
"""
assert
current
>=
1
assert
final
>=
current
# We always include the first two pages, last two pages, and
# two pages either side of the current page.
included
=
set
((
1
,
current
-
1
,
current
,
current
+
1
,
final
))
# If the break would only exclude a single page number then we
# may as well include the page number instead of the break.
if
current
==
4
:
included
.
add
(
2
)
if
current
==
final
-
3
:
included
.
add
(
final
-
1
)
# Now sort the page numbers and drop anything outside the limits.
included
=
[
idx
for
idx
in
sorted
(
list
(
included
))
if
idx
>
0
and
idx
<=
final
]
# Finally insert any `...` breaks
if
current
>
4
:
included
.
insert
(
1
,
None
)
if
current
<
final
-
3
:
included
.
insert
(
len
(
included
)
-
1
,
None
)
return
included
PageLink
=
namedtuple
(
'PageLink'
,
[
'url'
,
'number'
,
'is_active'
,
'is_break'
])
class
BasePagination
(
object
):
class
BasePagination
(
object
):
def
paginate_queryset
(
self
,
queryset
,
request
,
view
):
def
paginate_queryset
(
self
,
queryset
,
request
,
view
):
raise
NotImplemented
(
'paginate_queryset() must be implemented.'
)
raise
NotImplemented
(
'paginate_queryset() must be implemented.'
)
...
@@ -66,6 +113,8 @@ class PageNumberPagination(BasePagination):
...
@@ -66,6 +113,8 @@ class PageNumberPagination(BasePagination):
# Only relevant if 'paginate_by_param' has also been set.
# Only relevant if 'paginate_by_param' has also been set.
max_paginate_by
=
api_settings
.
MAX_PAGINATE_BY
max_paginate_by
=
api_settings
.
MAX_PAGINATE_BY
template
=
'rest_framework/pagination/numbers.html'
def
paginate_queryset
(
self
,
queryset
,
request
,
view
):
def
paginate_queryset
(
self
,
queryset
,
request
,
view
):
"""
"""
Paginate a queryset if required, either returning a
Paginate a queryset if required, either returning a
...
@@ -104,6 +153,8 @@ class PageNumberPagination(BasePagination):
...
@@ -104,6 +153,8 @@ class PageNumberPagination(BasePagination):
)
)
raise
NotFound
(
msg
)
raise
NotFound
(
msg
)
# Indicate that the browsable API should display pagination controls.
self
.
mark_as_used
=
True
self
.
request
=
request
self
.
request
=
request
return
self
.
page
return
self
.
page
...
@@ -139,8 +190,45 @@ class PageNumberPagination(BasePagination):
...
@@ -139,8 +190,45 @@ class PageNumberPagination(BasePagination):
return
None
return
None
url
=
self
.
request
.
build_absolute_uri
()
url
=
self
.
request
.
build_absolute_uri
()
page_number
=
self
.
page
.
previous_page_number
()
page_number
=
self
.
page
.
previous_page_number
()
if
page_number
==
1
:
return
remove_query_param
(
url
,
self
.
page_query_param
)
return
replace_query_param
(
url
,
self
.
page_query_param
,
page_number
)
return
replace_query_param
(
url
,
self
.
page_query_param
,
page_number
)
def
to_html
(
self
):
current
=
self
.
page
.
number
final
=
self
.
page
.
paginator
.
num_pages
page_links
=
[]
base_url
=
self
.
request
.
build_absolute_uri
()
for
page_number
in
_get_displayed_page_numbers
(
current
,
final
):
if
page_number
is
None
:
page_link
=
PageLink
(
url
=
None
,
number
=
None
,
is_active
=
False
,
is_break
=
True
)
else
:
if
page_number
==
1
:
url
=
remove_query_param
(
base_url
,
self
.
page_query_param
)
else
:
url
=
replace_query_param
(
url
,
self
.
page_query_param
,
page_number
)
page_link
=
PageLink
(
url
=
url
,
number
=
page_number
,
is_active
=
(
page_number
==
current
),
is_break
=
False
)
page_links
.
append
(
page_link
)
template
=
loader
.
get_template
(
self
.
template
)
context
=
Context
({
'previous_url'
:
self
.
get_previous_link
(),
'next_url'
:
self
.
get_next_link
(),
'page_links'
:
page_links
})
return
template
.
render
(
context
)
class
LimitOffsetPagination
(
BasePagination
):
class
LimitOffsetPagination
(
BasePagination
):
"""
"""
...
...
rest_framework/renderers.py
View file @
3833a5bb
...
@@ -592,6 +592,7 @@ class BrowsableAPIRenderer(BaseRenderer):
...
@@ -592,6 +592,7 @@ class BrowsableAPIRenderer(BaseRenderer):
'description'
:
self
.
get_description
(
view
),
'description'
:
self
.
get_description
(
view
),
'name'
:
self
.
get_name
(
view
),
'name'
:
self
.
get_name
(
view
),
'version'
:
VERSION
,
'version'
:
VERSION
,
'pager'
:
getattr
(
view
,
'pager'
,
None
),
'breadcrumblist'
:
self
.
get_breadcrumbs
(
request
),
'breadcrumblist'
:
self
.
get_breadcrumbs
(
request
),
'allowed_methods'
:
view
.
allowed_methods
,
'allowed_methods'
:
view
.
allowed_methods
,
'available_formats'
:
[
renderer_cls
.
format
for
renderer_cls
in
view
.
renderer_classes
],
'available_formats'
:
[
renderer_cls
.
format
for
renderer_cls
in
view
.
renderer_classes
],
...
...
rest_framework/static/rest_framework/css/bootstrap-tweaks.css
View file @
3833a5bb
...
@@ -185,10 +185,6 @@ body a:hover {
...
@@ -185,10 +185,6 @@ body a:hover {
color
:
#c20000
;
color
:
#c20000
;
}
}
#content
a
span
{
text-decoration
:
underline
;
}
.request-info
{
.request-info
{
clear
:
both
;
clear
:
both
;
}
}
rest_framework/templates/rest_framework/base.html
View file @
3833a5bb
...
@@ -119,9 +119,18 @@
...
@@ -119,9 +119,18 @@
<div
class=
"page-header"
>
<div
class=
"page-header"
>
<h1>
{{ name }}
</h1>
<h1>
{{ name }}
</h1>
</div>
</div>
<div
style=
"float:left"
>
{% block description %}
{% block description %}
{{ description }}
{{ description }}
{% endblock %}
{% endblock %}
</div>
{% if pager.mark_as_used %}
<nav
style=
"float: right"
>
{% get_pagination_html pager %}
</nav>
{% endif %}
<div
class=
"request-info"
style=
"clear: both"
>
<div
class=
"request-info"
style=
"clear: both"
>
<pre
class=
"prettyprint"
><b>
{{ request.method }}
</b>
{{ request.get_full_path }}
</pre>
<pre
class=
"prettyprint"
><b>
{{ request.method }}
</b>
{{ request.get_full_path }}
</pre>
</div>
</div>
...
...
rest_framework/templates/rest_framework/pagination/numbers.html
0 → 100644
View file @
3833a5bb
<ul
class=
"pagination"
style=
"margin: 5px 0 10px 0"
>
{% if previous_url %}
<li><a
href=
"{{ previous_url }}"
aria-label=
"Previous"
><span
aria-hidden=
"true"
>
«
</span></a></li>
{% else %}
<li
class=
"disabled"
><a
href=
"#"
aria-label=
"Previous"
><span
aria-hidden=
"true"
>
«
</span></a></li>
{% endif %}
{% for page_link in page_links %}
{% if page_link.is_break %}
<li
class=
"disabled"
>
<a
href=
"#"
><span
aria-hidden=
"true"
>
…
</span></a>
</li>
{% else %}
{% if page_link.is_active %}
<li
class=
"active"
><a
href=
"{{ page_link.url }}"
>
{{ page_link.number }}
</a></li>
{% else %}
<li><a
href=
"{{ page_link.url }}"
>
{{ page_link.number }}
</a></li>
{% endif %}
{% endif %}
{% endfor %}
{% if next_url %}
<li><a
href=
"{{ next_url }}"
aria-label=
"Next"
><span
aria-hidden=
"true"
>
»
</span></a></li>
{% else %}
<li
class=
"disabled"
><a
href=
"#"
aria-label=
"Next"
><span
aria-hidden=
"true"
>
»
</span></a></li>
{% endif %}
</ul>
rest_framework/templatetags/rest_framework.py
View file @
3833a5bb
...
@@ -26,6 +26,23 @@ def replace_query_param(url, key, val):
...
@@ -26,6 +26,23 @@ def replace_query_param(url, key, val):
return
urlparse
.
urlunsplit
((
scheme
,
netloc
,
path
,
query
,
fragment
))
return
urlparse
.
urlunsplit
((
scheme
,
netloc
,
path
,
query
,
fragment
))
def
remove_query_param
(
url
,
key
):
"""
Given a URL and a key/val pair, set or replace an item in the query
parameters of the URL, and return the new URL.
"""
(
scheme
,
netloc
,
path
,
query
,
fragment
)
=
urlparse
.
urlsplit
(
url
)
query_dict
=
QueryDict
(
query
)
.
copy
()
query_dict
.
pop
(
key
,
None
)
query
=
query_dict
.
urlencode
()
return
urlparse
.
urlunsplit
((
scheme
,
netloc
,
path
,
query
,
fragment
))
@register.simple_tag
def
get_pagination_html
(
pager
):
return
pager
.
to_html
()
# Regex for adding classes to html snippets
# Regex for adding classes to html snippets
class_re
=
re
.
compile
(
r'(?<=class=["\'])(.*)(?=["\'])'
)
class_re
=
re
.
compile
(
r'(?<=class=["\'])(.*)(?=["\'])'
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment