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
aeb57913
Commit
aeb57913
authored
Aug 27, 2015
by
Tom Christie
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Filter HTML refinments
parent
ea630bf3
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
134 additions
and
48 deletions
+134
-48
rest_framework/compat.py
+8
-0
rest_framework/filters.py
+85
-36
rest_framework/renderers.py
+2
-1
rest_framework/templates/rest_framework/admin.html
+9
-0
rest_framework/templates/rest_framework/filters/django_filter.html
+5
-4
rest_framework/templates/rest_framework/filters/django_filter_crispyforms.html
+4
-0
rest_framework/templates/rest_framework/filters/ordering.html
+10
-7
rest_framework/templates/rest_framework/filters/search.html
+11
-0
No files found.
rest_framework/compat.py
View file @
aeb57913
...
@@ -80,6 +80,14 @@ try:
...
@@ -80,6 +80,14 @@ try:
except
ImportError
:
except
ImportError
:
django_filters
=
None
django_filters
=
None
# django-crispy-forms is optional
try
:
import
crispy_forms
except
ImportError
:
crispy_forms
=
None
if
django
.
VERSION
>=
(
1
,
6
):
if
django
.
VERSION
>=
(
1
,
6
):
def
clean_manytomany_helptext
(
text
):
def
clean_manytomany_helptext
(
text
):
return
text
return
text
...
...
rest_framework/filters.py
View file @
aeb57913
...
@@ -7,17 +7,57 @@ from __future__ import unicode_literals
...
@@ -7,17 +7,57 @@ from __future__ import unicode_literals
import
operator
import
operator
from
functools
import
reduce
from
functools
import
reduce
from
django.conf
import
settings
from
django.core.exceptions
import
ImproperlyConfigured
from
django.core.exceptions
import
ImproperlyConfigured
from
django.db
import
models
from
django.db
import
models
from
django.template
import
Context
,
Template
,
loader
from
django.template
import
Context
,
Template
,
loader
from
django.utils
import
six
from
django.utils
import
six
from
rest_framework.compat
import
(
from
rest_framework.compat
import
(
distinct
,
django_filters
,
get_model_name
,
guardian
crispy_forms
,
distinct
,
django_filters
,
get_model_name
,
guardian
)
)
from
rest_framework.settings
import
api_settings
from
rest_framework.settings
import
api_settings
FilterSet
=
django_filters
and
django_filters
.
FilterSet
or
None
if
'crispy_forms'
in
settings
.
INSTALLED_APPS
and
crispy_forms
and
django_filters
:
# If django-crispy-forms is installed, use it to get a bootstrap3 rendering
# of the DjangoFilterBackend controls when displayed as HTML.
from
crispy_forms.helper
import
FormHelper
from
crispy_forms.layout
import
Field
,
Fieldset
,
Layout
,
Submit
class
FilterSet
(
django_filters
.
FilterSet
):
def
__init__
(
self
,
*
args
,
**
kwargs
):
super
(
FilterSet
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
for
field
in
self
.
form
.
fields
.
values
():
field
.
help_text
=
None
layout_components
=
list
(
self
.
form
.
fields
.
keys
())
+
[
Submit
(
''
,
'Submit'
,
css_class
=
'btn-default'
),
]
helper
=
FormHelper
()
helper
.
form_method
=
'GET'
helper
.
template_pack
=
'bootstrap3'
helper
.
layout
=
Layout
(
*
layout_components
)
self
.
form
.
helper
=
helper
filter_template
=
'rest_framework/filters/django_filter_crispyforms.html'
elif
django_filters
:
# If django-crispy-forms is not installed, use the standard
# 'form.as_p' rendering when DjangoFilterBackend is displayed as HTML.
class
FilterSet
(
django_filters
.
FilterSet
):
def
__init__
(
self
,
*
args
,
**
kwargs
):
super
(
FilterSet
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
for
field
in
self
.
form
.
fields
.
values
():
field
.
help_text
=
None
filter_template
=
'rest_framework/filters/django_filter.html'
else
:
FilterSet
=
None
filter_template
=
None
class
BaseFilterBackend
(
object
):
class
BaseFilterBackend
(
object
):
...
@@ -37,7 +77,7 @@ class DjangoFilterBackend(BaseFilterBackend):
...
@@ -37,7 +77,7 @@ class DjangoFilterBackend(BaseFilterBackend):
A filter backend that uses django-filter.
A filter backend that uses django-filter.
"""
"""
default_filter_set
=
FilterSet
default_filter_set
=
FilterSet
template
=
'rest_framework/filters/django_filter.html'
template
=
filter_template
def
__init__
(
self
):
def
__init__
(
self
):
assert
django_filters
,
'Using DjangoFilterBackend, but django-filter is not installed'
assert
django_filters
,
'Using DjangoFilterBackend, but django-filter is not installed'
...
@@ -59,33 +99,11 @@ class DjangoFilterBackend(BaseFilterBackend):
...
@@ -59,33 +99,11 @@ class DjangoFilterBackend(BaseFilterBackend):
return
filter_class
return
filter_class
if
filter_fields
:
if
filter_fields
:
from
crispy_forms.helper
import
FormHelper
class
AutoFilterSet
(
FilterSet
):
from
crispy_forms.layout
import
Field
,
Fieldset
,
Layout
,
Submit
class
AutoFilterSet
(
self
.
default_filter_set
):
class
Meta
:
class
Meta
:
model
=
queryset
.
model
model
=
queryset
.
model
fields
=
filter_fields
fields
=
filter_fields
@property
def
form
(
self
):
self
.
_form
=
super
(
AutoFilterSet
,
self
)
.
form
for
field
in
self
.
_form
.
fields
.
values
():
field
.
help_text
=
None
layout_components
=
filter_fields
+
[
Submit
(
''
,
'Apply'
,
css_class
=
'btn-default'
),
]
helper
=
FormHelper
()
helper
.
form_method
=
'get'
helper
.
form_action
=
'.'
helper
.
template_pack
=
'bootstrap3'
helper
.
layout
=
Layout
(
*
layout_components
)
self
.
_form
.
helper
=
helper
return
self
.
_form
return
AutoFilterSet
return
AutoFilterSet
return
None
return
None
...
@@ -111,6 +129,7 @@ class DjangoFilterBackend(BaseFilterBackend):
...
@@ -111,6 +129,7 @@ class DjangoFilterBackend(BaseFilterBackend):
class
SearchFilter
(
BaseFilterBackend
):
class
SearchFilter
(
BaseFilterBackend
):
# The URL query parameter used for the search.
# The URL query parameter used for the search.
search_param
=
api_settings
.
SEARCH_PARAM
search_param
=
api_settings
.
SEARCH_PARAM
template
=
'rest_framework/filters/search.html'
def
get_search_terms
(
self
,
request
):
def
get_search_terms
(
self
,
request
):
"""
"""
...
@@ -134,7 +153,6 @@ class SearchFilter(BaseFilterBackend):
...
@@ -134,7 +153,6 @@ class SearchFilter(BaseFilterBackend):
def
filter_queryset
(
self
,
request
,
queryset
,
view
):
def
filter_queryset
(
self
,
request
,
queryset
,
view
):
search_fields
=
getattr
(
view
,
'search_fields'
,
None
)
search_fields
=
getattr
(
view
,
'search_fields'
,
None
)
search_terms
=
self
.
get_search_terms
(
request
)
search_terms
=
self
.
get_search_terms
(
request
)
if
not
search_fields
or
not
search_terms
:
if
not
search_fields
or
not
search_terms
:
...
@@ -158,6 +176,19 @@ class SearchFilter(BaseFilterBackend):
...
@@ -158,6 +176,19 @@ class SearchFilter(BaseFilterBackend):
# in the resulting queryset.
# in the resulting queryset.
return
distinct
(
queryset
,
base
)
return
distinct
(
queryset
,
base
)
def
to_html
(
self
,
request
,
queryset
,
view
):
if
not
getattr
(
view
,
'search_fields'
,
None
):
return
''
term
=
self
.
get_search_terms
(
request
)
term
=
term
[
0
]
if
term
else
''
context
=
Context
({
'param'
:
self
.
search_param
,
'term'
:
term
})
template
=
loader
.
get_template
(
self
.
template
)
return
template
.
render
(
context
)
class
OrderingFilter
(
BaseFilterBackend
):
class
OrderingFilter
(
BaseFilterBackend
):
# The URL query parameter used for the ordering.
# The URL query parameter used for the ordering.
...
@@ -200,19 +231,30 @@ class OrderingFilter(BaseFilterBackend):
...
@@ -200,19 +231,30 @@ class OrderingFilter(BaseFilterBackend):
"'serializer_class' or 'ordering_fields' attribute."
)
"'serializer_class' or 'ordering_fields' attribute."
)
raise
ImproperlyConfigured
(
msg
%
self
.
__class__
.
__name__
)
raise
ImproperlyConfigured
(
msg
%
self
.
__class__
.
__name__
)
valid_fields
=
[
valid_fields
=
[
field
.
source
or
field_name
(
field
.
source
or
field_name
,
field
.
label
)
for
field_name
,
field
in
serializer_class
()
.
fields
.
items
()
for
field_name
,
field
in
serializer_class
()
.
fields
.
items
()
if
not
getattr
(
field
,
'write_only'
,
False
)
if
not
getattr
(
field
,
'write_only'
,
False
)
and
not
field
.
source
==
'*'
]
]
elif
valid_fields
==
'__all__'
:
elif
valid_fields
==
'__all__'
:
# View explicitly allows filtering on any model field
# View explicitly allows filtering on any model field
valid_fields
=
[
field
.
name
for
field
in
queryset
.
model
.
_meta
.
fields
]
valid_fields
=
[
valid_fields
+=
queryset
.
query
.
aggregates
.
keys
()
(
field
.
name
,
field
.
label
)
for
field
in
queryset
.
model
.
_meta
.
fields
]
valid_fields
+=
[
(
key
,
key
.
title
()
.
split
(
'__'
))
for
key
in
queryset
.
query
.
aggregates
.
keys
()
]
else
:
valid_fields
=
[
(
item
,
item
)
if
isinstance
(
item
,
six
.
string_types
)
else
item
for
item
in
valid_fields
]
return
valid_fields
return
valid_fields
def
remove_invalid_fields
(
self
,
queryset
,
fields
,
view
):
def
remove_invalid_fields
(
self
,
queryset
,
fields
,
view
):
valid_fields
=
self
.
get_valid_fields
(
queryset
,
view
)
valid_fields
=
[
item
[
0
]
for
item
in
self
.
get_valid_fields
(
queryset
,
view
)]
return
[
term
for
term
in
fields
if
term
.
lstrip
(
'-'
)
in
valid_fields
]
return
[
term
for
term
in
fields
if
term
.
lstrip
(
'-'
)
in
valid_fields
]
def
filter_queryset
(
self
,
request
,
queryset
,
view
):
def
filter_queryset
(
self
,
request
,
queryset
,
view
):
...
@@ -224,10 +266,17 @@ class OrderingFilter(BaseFilterBackend):
...
@@ -224,10 +266,17 @@ class OrderingFilter(BaseFilterBackend):
return
queryset
return
queryset
def
get_template_context
(
self
,
request
,
queryset
,
view
):
def
get_template_context
(
self
,
request
,
queryset
,
view
):
#default_tuple = self.get_default_ordering()
current
=
self
.
get_ordering
(
request
,
queryset
,
view
)
#default = None if default_tuple is None else default_tuple[0]
current
=
None
if
current
is
None
else
current
[
0
]
{
options
=
[]
'options'
:
self
.
get_valid_fields
(
queryset
,
view
),
for
key
,
label
in
self
.
get_valid_fields
(
queryset
,
view
):
options
.
append
((
key
,
'
%
s - ascending'
%
label
))
options
.
append
((
'-'
+
key
,
'
%
s - descending'
%
label
))
return
{
'request'
:
request
,
'current'
:
current
,
'param'
:
self
.
ordering_param
,
'options'
:
options
,
}
}
def
to_html
(
self
,
request
,
queryset
,
view
):
def
to_html
(
self
,
request
,
queryset
,
view
):
...
...
rest_framework/renderers.py
View file @
aeb57913
...
@@ -610,7 +610,8 @@ class BrowsableAPIRenderer(BaseRenderer):
...
@@ -610,7 +610,8 @@ class BrowsableAPIRenderer(BaseRenderer):
for
backend
in
view
.
filter_backends
:
for
backend
in
view
.
filter_backends
:
if
hasattr
(
backend
,
'to_html'
):
if
hasattr
(
backend
,
'to_html'
):
html
=
backend
()
.
to_html
(
request
,
queryset
,
view
)
html
=
backend
()
.
to_html
(
request
,
queryset
,
view
)
elements
.
append
(
html
)
if
html
:
elements
.
append
(
html
)
if
not
elements
:
if
not
elements
:
return
return
...
...
rest_framework/templates/rest_framework/admin.html
View file @
aeb57913
...
@@ -113,6 +113,13 @@
...
@@ -113,6 +113,13 @@
</form>
</form>
{% endif %}
{% endif %}
{% if filter_form %}
<button
style=
"float: right; margin-right: 10px"
data-toggle=
"modal"
data-target=
"#filtersModal"
class=
"btn btn-default"
>
<span
class=
"glyphicon glyphicon-wrench"
aria-hidden=
"true"
></span>
Filters
</button>
{% endif %}
<div
class=
"content-main"
>
<div
class=
"content-main"
>
<div
class=
"page-header"
>
<div
class=
"page-header"
>
<h1>
{{ name }}
</h1>
<h1>
{{ name }}
</h1>
...
@@ -220,6 +227,8 @@
...
@@ -220,6 +227,8 @@
</div>
</div>
{% endif %}
{% endif %}
{% if filter_form %}{{ filter_form }}{% endif %}
{% block script %}
{% block script %}
<script
src=
"{% static "
rest_framework
/
js
/
jquery-1
.
8
.
1-min
.
js
"
%}"
></script>
<script
src=
"{% static "
rest_framework
/
js
/
jquery-1
.
8
.
1-min
.
js
"
%}"
></script>
<script
src=
"{% static "
rest_framework
/
js
/
bootstrap
.
min
.
js
"
%}"
></script>
<script
src=
"{% static "
rest_framework
/
js
/
bootstrap
.
min
.
js
"
%}"
></script>
...
...
rest_framework/templates/rest_framework/filters/django_filter.html
View file @
aeb57913
{% load crispy_forms_tags %}
<h2>
Field filters
</h2>
<form
class=
"form"
action=
""
method=
"get"
>
<h2>
Field search
</h2>
{{ filter.form.as_p }}
{% crispy filter.form %}
<button
type=
"submit"
class=
"btn btn-primary"
>
Submit
</button>
</form>
rest_framework/templates/rest_framework/filters/django_filter_crispyforms.html
0 → 100644
View file @
aeb57913
{% load crispy_forms_tags %}
<h2>
Field filters
</h2>
{% crispy filter.form %}
rest_framework/templates/rest_framework/filters/ordering.html
View file @
aeb57913
{% load rest_framework %}
<h2>
Ordering
</h2>
<h2>
Ordering
</h2>
<div
class=
"list-group"
>
<div
class=
"list-group"
>
<a
href=
"."
class=
"list-group-item active"
>
{% for key, label in options %}
Most recently created
{% if key == current %}
<span
class=
"glyphicon glyphicon-ok"
style=
"float: right"
aria-hidden=
"true"
></span>
<a
href=
"{% add_query_param request param key %}"
class=
"list-group-item active"
>
</a>
<span
class=
"glyphicon glyphicon-ok"
style=
"float: right"
aria-hidden=
"true"
></span>
{{ label }}
<a
href=
"."
class=
"list-group-item"
>
Least recently created
</a>
</a>
<a
href=
"."
class=
"list-group-item"
>
Username ascending
</a>
{% else %}
<a
href=
"."
class=
"list-group-item"
>
Username descending
</a>
<a
href=
"{% add_query_param request param key %}"
class=
"list-group-item"
>
{{ label }}
</a>
{% endif %}
{% endfor %}
</div>
</div>
rest_framework/templates/rest_framework/filters/search.html
0 → 100644
View file @
aeb57913
<h2>
Search
</h2>
<form
class=
"form-inline"
>
<div
class=
"form-group"
>
<div
class=
"input-group"
>
<input
type=
"text"
class=
"form-control"
style=
"width: 350px"
name=
"{{ param }}"
value=
"{{ term }}"
>
<span
class=
"input-group-btn"
>
<button
class=
"btn btn-default"
type=
"submit"
><span
class=
"glyphicon glyphicon-search"
aria-hidden=
"true"
></span>
Search
</button>
</span>
</div>
</div>
</form>
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