1
1
import re
2
-
3
2
from django import template
4
3
from django .template import loader
5
4
from django .urls import NoReverseMatch , reverse
13
12
14
13
register = template .Library ()
15
14
16
- # Regex for adding classes to html snippets
17
- class_re = re .compile (r'(?<=class=["\'])(.*)(?=["\'])' )
18
-
15
+ # Precompile regex patterns
16
+ class_re = re .compile (r'(?<=class=["\'])(.*?)(?=["\'])' )
19
17
20
18
@register .tag (name = 'code' )
21
19
def highlight_code (parser , token ):
@@ -24,7 +22,6 @@ def highlight_code(parser, token):
24
22
parser .delete_first_token ()
25
23
return CodeNode (code , nodelist )
26
24
27
-
28
25
class CodeNode (template .Node ):
29
26
style = 'emacs'
30
27
@@ -36,56 +33,39 @@ def render(self, context):
36
33
text = self .nodelist .render (context )
37
34
return pygments_highlight (text , self .lang , self .style )
38
35
39
-
40
36
@register .filter ()
41
37
def with_location (fields , location ):
42
- return [
43
- field for field in fields
44
- if field .location == location
45
- ]
46
-
38
+ return [field for field in fields if field .location == location ]
47
39
48
40
@register .simple_tag
49
41
def form_for_link (link ):
50
42
import coreschema
51
- properties = {
52
- field .name : field .schema or coreschema .String ()
53
- for field in link .fields
54
- }
55
- required = [
56
- field .name
57
- for field in link .fields
58
- if field .required
59
- ]
43
+ properties = {field .name : field .schema or coreschema .String () for field in link .fields }
44
+ required = [field .name for field in link .fields if field .required ]
60
45
schema = coreschema .Object (properties = properties , required = required )
61
46
return mark_safe (coreschema .render_to_form (schema ))
62
47
63
-
64
48
@register .simple_tag
65
49
def render_markdown (markdown_text ):
66
50
if apply_markdown is None :
67
51
return markdown_text
68
52
return mark_safe (apply_markdown (markdown_text ))
69
53
70
-
71
54
@register .simple_tag
72
55
def get_pagination_html (pager ):
73
56
return pager .to_html ()
74
57
75
-
76
58
@register .simple_tag
77
59
def render_form (serializer , template_pack = None ):
78
60
style = {'template_pack' : template_pack } if template_pack else {}
79
61
renderer = HTMLFormRenderer ()
80
62
return renderer .render (serializer .data , None , {'style' : style })
81
63
82
-
83
64
@register .simple_tag
84
65
def render_field (field , style ):
85
66
renderer = style .get ('renderer' , HTMLFormRenderer ())
86
67
return renderer .render_field (field , style )
87
68
88
-
89
69
@register .simple_tag
90
70
def optional_login (request ):
91
71
"""
@@ -95,13 +75,10 @@ def optional_login(request):
95
75
login_url = reverse ('rest_framework:login' )
96
76
except NoReverseMatch :
97
77
return ''
98
-
99
78
snippet = "<li><a href='{href}?next={next}'>Log in</a></li>"
100
79
snippet = format_html (snippet , href = login_url , next = escape (request .path ))
101
-
102
80
return mark_safe (snippet )
103
81
104
-
105
82
@register .simple_tag
106
83
def optional_docs_login (request ):
107
84
"""
@@ -111,13 +88,10 @@ def optional_docs_login(request):
111
88
login_url = reverse ('rest_framework:login' )
112
89
except NoReverseMatch :
113
90
return 'log in'
114
-
115
91
snippet = "<a href='{href}?next={next}'>log in</a>"
116
92
snippet = format_html (snippet , href = login_url , next = escape (request .path ))
117
-
118
93
return mark_safe (snippet )
119
94
120
-
121
95
@register .simple_tag
122
96
def optional_logout (request , user , csrf_token ):
123
97
"""
@@ -128,7 +102,6 @@ def optional_logout(request, user, csrf_token):
128
102
except NoReverseMatch :
129
103
snippet = format_html ('<li class="navbar-text">{user}</li>' , user = escape (user ))
130
104
return mark_safe (snippet )
131
-
132
105
snippet = """<li class="dropdown">
133
106
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
134
107
{user}
@@ -143,11 +116,9 @@ def optional_logout(request, user, csrf_token):
143
116
</li>
144
117
</ul>
145
118
</li>"""
146
- snippet = format_html (snippet , user = escape (user ), href = logout_url ,
147
- next = escape (request .path ), csrf_token = csrf_token )
119
+ snippet = format_html (snippet , user = escape (user ), href = logout_url , next = escape (request .path ), csrf_token = csrf_token )
148
120
return mark_safe (snippet )
149
121
150
-
151
122
@register .simple_tag
152
123
def add_query_param (request , key , val ):
153
124
"""
@@ -157,170 +128,98 @@ def add_query_param(request, key, val):
157
128
uri = iri_to_uri (iri )
158
129
return escape (replace_query_param (uri , key , val ))
159
130
160
-
161
131
@register .filter
162
132
def as_string (value ):
163
- if value is None :
164
- return ''
165
- return '%s' % value
166
-
133
+ return '' if value is None else '%s' % value
167
134
168
135
@register .filter
169
136
def as_list_of_strings (value ):
170
- return [
171
- '' if (item is None ) else ('%s' % item )
172
- for item in value
173
- ]
174
-
137
+ return ['' if item is None else '%s' % item for item in value ]
175
138
176
139
@register .filter
177
140
def add_class (value , css_class ):
178
- """
179
- https://stackoverflow.com/questions/4124220/django-adding-css-classes-when-rendering-form-fields-in-a-template
180
-
181
- Inserts classes into template variables that contain HTML tags,
182
- useful for modifying forms without needing to change the Form objects.
183
-
184
- Usage:
185
-
186
- {{ field.label_tag|add_class:"control-label" }}
187
-
188
- In the case of REST Framework, the filter is used to add Bootstrap-specific
189
- classes to the forms.
190
- """
191
141
html = str (value )
192
142
match = class_re .search (html )
193
143
if match :
194
- m = re .search (r'^%s$|^%s\s|\s%s\s|\s%s$' % (css_class , css_class ,
195
- css_class , css_class ),
196
- match .group (1 ))
197
- if not m :
198
- return mark_safe (class_re .sub (match .group (1 ) + " " + css_class ,
199
- html ))
144
+ classes = match .group (1 )
145
+ if css_class not in classes .split ():
146
+ classes += f" { css_class } "
147
+ html = class_re .sub (classes , html )
200
148
else :
201
- return mark_safe (html .replace ('>' , ' class="%s">' % css_class , 1 ))
202
- return value
203
-
149
+ html = html .replace ('>' , f' class="{ css_class } ">' , 1 )
150
+ return mark_safe (html )
204
151
205
152
@register .filter
206
153
def format_value (value ):
207
154
if getattr (value , 'is_hyperlink' , False ):
208
155
name = str (value .obj )
209
- return mark_safe ('<a href=%s>%s </a>' % ( value , escape ( name )) )
156
+ return mark_safe (f '<a href={ value } > { escape ( name ) } </a>' )
210
157
if value is None or isinstance (value , bool ):
211
- return mark_safe ('<code>%s </code>' % { True : 'true' , False : 'false' , None : 'null' }[ value ] )
212
- elif isinstance (value , list ):
158
+ return mark_safe (f '<code>{ value } </code>' )
159
+ if isinstance (value , list ):
213
160
if any (isinstance (item , (list , dict )) for item in value ):
214
161
template = loader .get_template ('rest_framework/admin/list_value.html' )
215
162
else :
216
163
template = loader .get_template ('rest_framework/admin/simple_list_value.html' )
217
- context = {'value' : value }
218
- return template .render (context )
219
- elif isinstance (value , dict ):
164
+ return template .render ({'value' : value })
165
+ if isinstance (value , dict ):
220
166
template = loader .get_template ('rest_framework/admin/dict_value.html' )
221
- context = {'value' : value }
222
- return template .render (context )
223
- elif isinstance (value , str ):
224
- if (
225
- (value .startswith ('http:' ) or value .startswith ('https:' ) or value .startswith ('/' )) and not
226
- re .search (r'\s' , value )
227
- ):
228
- return mark_safe ('<a href="{value}">{value}</a>' .format (value = escape (value )))
229
- elif '@' in value and not re .search (r'\s' , value ):
230
- return mark_safe ('<a href="mailto:{value}">{value}</a>' .format (value = escape (value )))
231
- elif '\n ' in value :
232
- return mark_safe ('<pre>%s</pre>' % escape (value ))
167
+ return template .render ({'value' : value })
168
+ if isinstance (value , str ):
169
+ if (value .startswith ('http' ) or value .startswith ('/' )) and not re .search (r'\s' , value ):
170
+ return mark_safe (f'<a href="{ escape (value )} ">{ escape (value )} </a>' )
171
+ if '@' in value and not re .search (r'\s' , value ):
172
+ return mark_safe (f'<a href="mailto:{ escape (value )} ">{ escape (value )} </a>' )
173
+ if '\n ' in value :
174
+ return mark_safe (f'<pre>{ escape (value )} </pre>' )
233
175
return str (value )
234
176
235
-
236
177
@register .filter
237
178
def items (value ):
238
- """
239
- Simple filter to return the items of the dict. Useful when the dict may
240
- have a key 'items' which is resolved first in Django template dot-notation
241
- lookup. See issue #4931
242
- Also see: https://stackoverflow.com/questions/15416662/django-template-loop-over-dictionary-items-with-items-as-key
243
- """
244
- if value is None :
245
- # `{% for k, v in value.items %}` doesn't raise when value is None or
246
- # not in the context, so neither should `{% for k, v in value|items %}`
247
- return []
248
- return value .items ()
249
-
179
+ return [] if value is None else value .items ()
250
180
251
181
@register .filter
252
182
def data (value ):
253
- """
254
- Simple filter to access `data` attribute of object,
255
- specifically coreapi.Document.
256
-
257
- As per `items` filter above, allows accessing `document.data` when
258
- Document contains Link keyed-at "data".
259
-
260
- See issue #5395
261
- """
262
183
return value .data
263
184
264
-
265
185
@register .filter
266
186
def schema_links (section , sec_key = None ):
267
187
"""
268
188
Recursively find every link in a schema, even nested.
269
189
"""
270
- NESTED_FORMAT = '%s > %s' # this format is used in docs/js/api.js:normalizeKeys
190
+ NESTED_FORMAT = '%s > %s'
271
191
links = section .links
272
192
if section .data :
273
193
data = section .data .items ()
274
194
for sub_section_key , sub_section in data :
275
195
new_links = schema_links (sub_section , sec_key = sub_section_key )
276
196
links .update (new_links )
277
-
278
197
if sec_key is not None :
279
- new_links = {}
280
- for link_key , link in links .items ():
281
- new_key = NESTED_FORMAT % (sec_key , link_key )
282
- new_links .update ({new_key : link })
198
+ new_links = {NESTED_FORMAT % (sec_key , link_key ): link for link_key , link in links .items ()}
283
199
return new_links
284
-
285
200
return links
286
201
287
-
288
202
@register .filter
289
203
def add_nested_class (value ):
290
- if isinstance (value , dict ):
291
- return 'class=nested'
292
- if isinstance (value , list ) and any (isinstance (item , (list , dict )) for item in value ):
204
+ if isinstance (value , dict ) or (isinstance (value , list ) and any (isinstance (item , (list , dict )) for item in value )):
293
205
return 'class=nested'
294
206
return ''
295
207
296
-
297
- # Bunch of stuff cloned from urlize
298
- TRAILING_PUNCTUATION = ['.' , ',' , ':' , ';' , '.)' , '"' , "']" , "'}" , "'" ]
299
- WRAPPING_PUNCTUATION = [('(' , ')' ), ('<' , '>' ), ('[' , ']' ), ('<' , '>' ),
300
- ('"' , '"' ), ("'" , "'" )]
208
+ TRAILING_PUNCTUATION = ['.' , ',' , ':' , ';' , '.)' , '"' , "']" , "'}" ]
209
+ WRAPPING_PUNCTUATION = [('(' , ')' ), ('<' , '>' ), ('[' , ']' ), ('<' , '>' ), ('"' , '"' ), ("'" , "'" )]
301
210
word_split_re = re .compile (r'(\s+)' )
302
211
simple_url_re = re .compile (r'^https?://\[?\w' , re .IGNORECASE )
303
212
simple_url_2_re = re .compile (r'^www\.|^(?!http)\w[^@]+\.(com|edu|gov|int|mil|net|org)$' , re .IGNORECASE )
304
213
simple_email_re = re .compile (r'^\S+@\S+\.\S+$' )
305
214
306
-
307
215
def smart_urlquote_wrapper (matched_url ):
308
- """
309
- Simple wrapper for smart_urlquote. ValueError("Invalid IPv6 URL") can
310
- be raised here, see issue #1386
311
- """
312
216
try :
313
217
return smart_urlquote (matched_url )
314
218
except ValueError :
315
219
return None
316
220
317
-
318
221
@register .filter
319
222
def break_long_headers (header ):
320
- """
321
- Breaks headers longer than 160 characters (~page length)
322
- when possible (are comma separated)
323
- """
324
223
if len (header ) > 160 and ',' in header :
325
224
header = mark_safe ('<br> ' + ', <br>' .join (escape (header ).split (',' )))
326
225
return header
0 commit comments