7
7
import operator
8
8
from functools import reduce
9
9
10
+ from django .conf import settings
10
11
from django .core .exceptions import ImproperlyConfigured
11
12
from django .db import models
12
13
from django .template import Context , Template , loader
13
14
from django .utils import six
14
15
15
16
from rest_framework .compat import (
16
- distinct , django_filters , get_model_name , guardian
17
+ crispy_forms , distinct , django_filters , get_model_name , guardian
17
18
)
18
19
from rest_framework .settings import api_settings
19
20
20
- FilterSet = django_filters and django_filters .FilterSet or None
21
+
22
+ if 'crispy_forms' in settings .INSTALLED_APPS and crispy_forms and django_filters :
23
+ # If django-crispy-forms is installed, use it to get a bootstrap3 rendering
24
+ # of the DjangoFilterBackend controls when displayed as HTML.
25
+ from crispy_forms .helper import FormHelper
26
+ from crispy_forms .layout import Field , Fieldset , Layout , Submit
27
+
28
+ class FilterSet (django_filters .FilterSet ):
29
+ def __init__ (self , * args , ** kwargs ):
30
+ super (FilterSet , self ).__init__ (* args , ** kwargs )
31
+ for field in self .form .fields .values ():
32
+ field .help_text = None
33
+
34
+ layout_components = list (self .form .fields .keys ()) + [
35
+ Submit ('' , 'Submit' , css_class = 'btn-default' ),
36
+ ]
37
+
38
+ helper = FormHelper ()
39
+ helper .form_method = 'GET'
40
+ helper .template_pack = 'bootstrap3'
41
+ helper .layout = Layout (* layout_components )
42
+
43
+ self .form .helper = helper
44
+
45
+ filter_template = 'rest_framework/filters/django_filter_crispyforms.html'
46
+
47
+ elif django_filters :
48
+ # If django-crispy-forms is not installed, use the standard
49
+ # 'form.as_p' rendering when DjangoFilterBackend is displayed as HTML.
50
+ class FilterSet (django_filters .FilterSet ):
51
+ def __init__ (self , * args , ** kwargs ):
52
+ super (FilterSet , self ).__init__ (* args , ** kwargs )
53
+ for field in self .form .fields .values ():
54
+ field .help_text = None
55
+
56
+ filter_template = 'rest_framework/filters/django_filter.html'
57
+
58
+ else :
59
+ FilterSet = None
60
+ filter_template = None
21
61
22
62
23
63
class BaseFilterBackend (object ):
@@ -37,7 +77,7 @@ class DjangoFilterBackend(BaseFilterBackend):
37
77
A filter backend that uses django-filter.
38
78
"""
39
79
default_filter_set = FilterSet
40
- template = 'rest_framework/filters/django_filter.html'
80
+ template = filter_template
41
81
42
82
def __init__ (self ):
43
83
assert django_filters , 'Using DjangoFilterBackend, but django-filter is not installed'
@@ -59,33 +99,11 @@ def get_filter_class(self, view, queryset=None):
59
99
return filter_class
60
100
61
101
if filter_fields :
62
- from crispy_forms .helper import FormHelper
63
- from crispy_forms .layout import Field , Fieldset , Layout , Submit
64
-
65
- class AutoFilterSet (self .default_filter_set ):
102
+ class AutoFilterSet (FilterSet ):
66
103
class Meta :
67
104
model = queryset .model
68
105
fields = filter_fields
69
106
70
- @property
71
- def form (self ):
72
- self ._form = super (AutoFilterSet , self ).form
73
- for field in self ._form .fields .values ():
74
- field .help_text = None
75
- layout_components = filter_fields + [
76
- Submit ('' , 'Apply' , css_class = 'btn-default' ),
77
- ]
78
-
79
- helper = FormHelper ()
80
- helper .form_method = 'get'
81
- helper .form_action = '.'
82
- helper .template_pack = 'bootstrap3'
83
-
84
- helper .layout = Layout (* layout_components )
85
-
86
- self ._form .helper = helper
87
- return self ._form
88
-
89
107
return AutoFilterSet
90
108
91
109
return None
@@ -111,6 +129,7 @@ def to_html(self, request, queryset, view):
111
129
class SearchFilter (BaseFilterBackend ):
112
130
# The URL query parameter used for the search.
113
131
search_param = api_settings .SEARCH_PARAM
132
+ template = 'rest_framework/filters/search.html'
114
133
115
134
def get_search_terms (self , request ):
116
135
"""
@@ -134,7 +153,6 @@ def construct_search(self, field_name):
134
153
135
154
def filter_queryset (self , request , queryset , view ):
136
155
search_fields = getattr (view , 'search_fields' , None )
137
-
138
156
search_terms = self .get_search_terms (request )
139
157
140
158
if not search_fields or not search_terms :
@@ -158,6 +176,19 @@ def filter_queryset(self, request, queryset, view):
158
176
# in the resulting queryset.
159
177
return distinct (queryset , base )
160
178
179
+ def to_html (self , request , queryset , view ):
180
+ if not getattr (view , 'search_fields' , None ):
181
+ return ''
182
+
183
+ term = self .get_search_terms (request )
184
+ term = term [0 ] if term else ''
185
+ context = Context ({
186
+ 'param' : self .search_param ,
187
+ 'term' : term
188
+ })
189
+ template = loader .get_template (self .template )
190
+ return template .render (context )
191
+
161
192
162
193
class OrderingFilter (BaseFilterBackend ):
163
194
# The URL query parameter used for the ordering.
@@ -200,19 +231,30 @@ def get_valid_fields(self, queryset, view):
200
231
"'serializer_class' or 'ordering_fields' attribute." )
201
232
raise ImproperlyConfigured (msg % self .__class__ .__name__ )
202
233
valid_fields = [
203
- field .source or field_name
234
+ ( field .source or field_name , field . label )
204
235
for field_name , field in serializer_class ().fields .items ()
205
- if not getattr (field , 'write_only' , False )
236
+ if not getattr (field , 'write_only' , False ) and not field . source == '*'
206
237
]
207
238
elif valid_fields == '__all__' :
208
239
# View explicitly allows filtering on any model field
209
- valid_fields = [field .name for field in queryset .model ._meta .fields ]
210
- valid_fields += queryset .query .aggregates .keys ()
240
+ valid_fields = [
241
+ (field .name , field .label )
242
+ for field in queryset .model ._meta .fields
243
+ ]
244
+ valid_fields += [
245
+ (key , key .title ().split ('__' ))
246
+ for key in queryset .query .aggregates .keys ()
247
+ ]
248
+ else :
249
+ valid_fields = [
250
+ (item , item ) if isinstance (item , six .string_types ) else item
251
+ for item in valid_fields
252
+ ]
211
253
212
254
return valid_fields
213
255
214
256
def remove_invalid_fields (self , queryset , fields , view ):
215
- valid_fields = self .get_valid_fields (queryset , view )
257
+ valid_fields = [ item [ 0 ] for item in self .get_valid_fields (queryset , view )]
216
258
return [term for term in fields if term .lstrip ('-' ) in valid_fields ]
217
259
218
260
def filter_queryset (self , request , queryset , view ):
@@ -224,10 +266,17 @@ def filter_queryset(self, request, queryset, view):
224
266
return queryset
225
267
226
268
def get_template_context (self , request , queryset , view ):
227
- #default_tuple = self.get_default_ordering()
228
- #default = None if default_tuple is None else default_tuple[0]
229
- {
230
- 'options' : self .get_valid_fields (queryset , view ),
269
+ current = self .get_ordering (request , queryset , view )
270
+ current = None if current is None else current [0 ]
271
+ options = []
272
+ for key , label in self .get_valid_fields (queryset , view ):
273
+ options .append ((key , '%s - ascending' % label ))
274
+ options .append (('-' + key , '%s - descending' % label ))
275
+ return {
276
+ 'request' : request ,
277
+ 'current' : current ,
278
+ 'param' : self .ordering_param ,
279
+ 'options' : options ,
231
280
}
232
281
233
282
def to_html (self , request , queryset , view ):
0 commit comments