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
d76e83dd
Commit
d76e83dd
authored
Jan 15, 2015
by
Tom Christie
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Tweaks, and add pagination controls for offset/limit.
parent
313aa727
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
120 additions
and
41 deletions
+120
-41
rest_framework/generics.py
+8
-8
rest_framework/pagination.py
+97
-30
rest_framework/renderers.py
+6
-1
rest_framework/static/rest_framework/css/bootstrap-tweaks.css
+7
-0
rest_framework/templates/rest_framework/base.html
+2
-2
No files found.
rest_framework/generics.py
View file @
d76e83dd
...
...
@@ -150,21 +150,21 @@ class GenericAPIView(views.APIView):
return
queryset
@property
def
pag
e
r
(
self
):
if
not
hasattr
(
self
,
'_pag
e
r'
):
def
pag
inato
r
(
self
):
if
not
hasattr
(
self
,
'_pag
inato
r'
):
if
self
.
pagination_class
is
None
:
self
.
_pag
e
r
=
None
self
.
_pag
inato
r
=
None
else
:
self
.
_pag
e
r
=
self
.
pagination_class
()
return
self
.
_pag
e
r
self
.
_pag
inato
r
=
self
.
pagination_class
()
return
self
.
_pag
inato
r
def
paginate_queryset
(
self
,
queryset
):
if
self
.
pag
e
r
is
None
:
if
self
.
pag
inato
r
is
None
:
return
queryset
return
self
.
pag
e
r
.
paginate_queryset
(
queryset
,
self
.
request
,
view
=
self
)
return
self
.
pag
inato
r
.
paginate_queryset
(
queryset
,
self
.
request
,
view
=
self
)
def
get_paginated_response
(
self
,
data
):
return
self
.
pag
e
r
.
get_paginated_response
(
data
)
return
self
.
pag
inato
r
.
get_paginated_response
(
data
)
# Concrete view classes that provide method handlers
...
...
rest_framework/pagination.py
View file @
d76e83dd
...
...
@@ -29,6 +29,15 @@ def _strict_positive_int(integer_string, cutoff=None):
return
ret
def
_divide_with_ceil
(
a
,
b
):
"""
Returns 'a' divded by 'b', with any remainder rounded up.
"""
if
a
%
b
:
return
(
a
/
b
)
+
1
return
a
/
b
def
_get_count
(
queryset
):
"""
Determine an object count, supporting either querysets or regular lists.
...
...
@@ -48,14 +57,21 @@ def _get_displayed_page_numbers(current, final):
current=14, final=16 -> [1, None, 13, 14, 15, 16]
This implementation gives one page to each side of the cursor,
for an implementation which gives two pages to each side of the cursor,
which is a copy of how GitHub treat pagination in their issue lists, see:
or two pages to the side when the cursor is at the edge, then
ensures that any breaks between non-continous page numbers never
remove only a single page.
For an alernativative implementation which gives two pages to each side of
the cursor, eg. as in GitHub issue list pagination, see:
https://gist.github.com/tomchristie/321140cebb1c4a558b15
"""
assert
current
>=
1
assert
final
>=
current
if
final
<=
5
:
return
range
(
1
,
final
+
1
)
# We always include the first two pages, last two pages, and
# two pages either side of the current page.
included
=
set
((
...
...
@@ -87,16 +103,46 @@ def _get_displayed_page_numbers(current, final):
return
included
def
_get_page_links
(
page_numbers
,
current
,
url_func
):
"""
Given a list of page numbers and `None` page breaks,
return a list of `PageLink` objects.
"""
page_links
=
[]
for
page_number
in
page_numbers
:
if
page_number
is
None
:
page_link
=
PageLink
(
url
=
None
,
number
=
None
,
is_active
=
False
,
is_break
=
True
)
else
:
page_link
=
PageLink
(
url
=
url_func
(
page_number
),
number
=
page_number
,
is_active
=
(
page_number
==
current
),
is_break
=
False
)
page_links
.
append
(
page_link
)
return
page_links
PageLink
=
namedtuple
(
'PageLink'
,
[
'url'
,
'number'
,
'is_active'
,
'is_break'
])
class
BasePagination
(
object
):
display_page_controls
=
False
def
paginate_queryset
(
self
,
queryset
,
request
,
view
):
raise
NotImplemented
(
'paginate_queryset() must be implemented.'
)
def
get_paginated_response
(
self
,
data
):
raise
NotImplemented
(
'get_paginated_response() must be implemented.'
)
def
to_html
(
self
):
raise
NotImplemented
(
'to_html() must be implemented to display page controls.'
)
class
PageNumberPagination
(
BasePagination
):
"""
...
...
@@ -161,8 +207,9 @@ class PageNumberPagination(BasePagination):
)
raise
NotFound
(
msg
)
# Indicate that the browsable API should display pagination controls.
self
.
mark_as_used
=
True
if
paginator
.
count
>
1
:
# The browsable API should display pagination controls.
self
.
display_page_controls
=
True
self
.
request
=
request
return
self
.
page
...
...
@@ -203,31 +250,17 @@ class PageNumberPagination(BasePagination):
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
)
def
page_number_to_url
(
page_number
):
if
page_number
==
1
:
return
remove_query_param
(
base_url
,
self
.
page_query_param
)
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
)
return
replace_query_param
(
base_url
,
self
.
page_query_param
,
page_number
)
current
=
self
.
page
.
number
final
=
self
.
page
.
paginator
.
num_pages
page_numbers
=
_get_displayed_page_numbers
(
current
,
final
)
page_links
=
_get_page_links
(
page_numbers
,
current
,
page_number_to_url
)
template
=
loader
.
get_template
(
self
.
template
)
context
=
Context
({
...
...
@@ -250,11 +283,15 @@ class LimitOffsetPagination(BasePagination):
offset_query_param
=
'offset'
max_limit
=
None
template
=
'rest_framework/pagination/numbers.html'
def
paginate_queryset
(
self
,
queryset
,
request
,
view
):
self
.
limit
=
self
.
get_limit
(
request
)
self
.
offset
=
self
.
get_offset
(
request
)
self
.
count
=
_get_count
(
queryset
)
self
.
request
=
request
if
self
.
count
>
self
.
limit
:
self
.
display_page_controls
=
True
return
queryset
[
self
.
offset
:
self
.
offset
+
self
.
limit
]
def
get_paginated_response
(
self
,
data
):
...
...
@@ -285,16 +322,45 @@ class LimitOffsetPagination(BasePagination):
except
(
KeyError
,
ValueError
):
return
0
def
get_next_link
(
self
,
page
):
def
get_next_link
(
self
):
if
self
.
offset
+
self
.
limit
>=
self
.
count
:
return
None
url
=
self
.
request
.
build_absolute_uri
()
offset
=
self
.
offset
+
self
.
limit
return
replace_query_param
(
url
,
self
.
offset_query_param
,
offset
)
def
get_previous_link
(
self
,
page
):
if
self
.
offset
-
self
.
limit
<
0
:
def
get_previous_link
(
self
):
if
self
.
offset
<=
0
:
return
None
url
=
self
.
request
.
build_absolute_uri
()
if
self
.
offset
-
self
.
limit
<=
0
:
return
remove_query_param
(
url
,
self
.
offset_query_param
)
offset
=
self
.
offset
-
self
.
limit
return
replace_query_param
(
url
,
self
.
offset_query_param
,
offset
)
def
to_html
(
self
):
base_url
=
self
.
request
.
build_absolute_uri
()
current
=
_divide_with_ceil
(
self
.
offset
,
self
.
limit
)
+
1
final
=
_divide_with_ceil
(
self
.
count
,
self
.
limit
)
def
page_number_to_url
(
page_number
):
if
page_number
==
1
:
return
remove_query_param
(
base_url
,
self
.
offset_query_param
)
else
:
offset
=
self
.
offset
+
((
page_number
-
current
)
*
self
.
limit
)
return
replace_query_param
(
base_url
,
self
.
offset_query_param
,
offset
)
page_numbers
=
_get_displayed_page_numbers
(
current
,
final
)
page_links
=
_get_page_links
(
page_numbers
,
current
,
page_number_to_url
)
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
)
\ No newline at end of file
rest_framework/renderers.py
View file @
d76e83dd
...
...
@@ -584,6 +584,11 @@ class BrowsableAPIRenderer(BaseRenderer):
renderer_content_type
+=
' ;
%
s'
%
renderer
.
charset
response_headers
[
'Content-Type'
]
=
renderer_content_type
if
hasattr
(
view
,
'paginator'
)
and
view
.
paginator
.
display_page_controls
:
paginator
=
view
.
paginator
else
:
paginator
=
None
context
=
{
'content'
:
self
.
get_content
(
renderer
,
data
,
accepted_media_type
,
renderer_context
),
'view'
:
view
,
...
...
@@ -592,7 +597,7 @@ class BrowsableAPIRenderer(BaseRenderer):
'description'
:
self
.
get_description
(
view
),
'name'
:
self
.
get_name
(
view
),
'version'
:
VERSION
,
'pag
er'
:
getattr
(
view
,
'pager'
,
None
)
,
'pag
inator'
:
paginator
,
'breadcrumblist'
:
self
.
get_breadcrumbs
(
request
),
'allowed_methods'
:
view
.
allowed_methods
,
'available_formats'
:
[
renderer_cls
.
format
for
renderer_cls
in
view
.
renderer_classes
],
...
...
rest_framework/static/rest_framework/css/bootstrap-tweaks.css
View file @
d76e83dd
...
...
@@ -60,6 +60,13 @@ a single block in the template.
color
:
#C20000
;
}
.pagination
>
.disabled
>
a
,
.pagination
>
.disabled
>
a
:hover
,
.pagination
>
.disabled
>
a
:focus
{
cursor
:
default
;
pointer-events
:
none
;
}
/*=== dabapps bootstrap styles ====*/
html
{
...
...
rest_framework/templates/rest_framework/base.html
View file @
d76e83dd
...
...
@@ -125,9 +125,9 @@
{% endblock %}
</div>
{% if pag
er.mark_as_used
%}
{% if pag
inator
%}
<nav
style=
"float: right"
>
{% get_pagination_html pag
e
r %}
{% get_pagination_html pag
inato
r %}
</nav>
{% endif %}
...
...
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