For my current project I wanted to have a form with a search term which lists a table of results. I’m not mormally one for presentation but in this case I wanted to highlight the search term in the results table.
Sanitizing input data
Of course, this is an instance of taking form-submitted data and then displaying it on a different page.
This is something I’d normally try to avoid doing, but I thought I’d take the opportunity to apply some input validation and sanitization and get used to applying such ideas in general.
Python has an HTML parser library that helps to make this task straightforward.
Add a subclass of HTMLParser like
from html.parser import HTMLParser class HTMLStripper(HTMLParser): convert_charrefs=True def __init__(self): self.reset() self.fed = [] def handle_data(self, d): self.fed.append(d) def handle_entityref(self, name): self.fed.append('&%s;' % name) def get_data(self): return ''.join(self.fed)
And apply the class using
def validate_text(str): s = HTMLStripper() s.feed(str) return("", s.get_data())
Custom filter
I had hoped that the Jinja2 replace filter would do the trick but it is only case-senstive so would work when the search term was capitalized at the beginning of the retrieved text.
The only way to handle all cases regardless of case would be to write my own filter.
There are three steps:
- writing a python function for the filter,
- registering it in the jinja2 environment, and,
- using it in a view template
Filter function
The filter function is a basic Python function that simply does a case insensitive search and replace of the word inside the text without changing the matching word but adding some highlighting HTML.
def highlight_text(text, word, cls): p = re.compile(word, re.IGNORECASE) m = p.search(text) if m: hstr = ("<span class='%s'>%s</span>" % (cls, m.group())) return(p.sub(hstr, text)) else: return(text)
The use of re.search is important here and the function won’t work as expected if re.match() is used.
Filter register
The filter is registered with Jinja2 using the following
application.jinja_env.filters['highlight_text'] = highlight_text
Jinja2 template
The custom filter is used in the same way as any other filter
<td>{{ search.text|highlight_text(search_term, 'highlight-cue')|safe }}</td>
The search.text is the first argument passed to the filter function; the search_term and CSS class name are the others, in order.
References
The following were very helpful in developing the solution described on this page.
- https://stackoverflow.com/questions/753052/strip-html-from-strings-in-python (comment 16) – for sanitizing the input
- https://jinja.palletsprojects.com/en/2.10.x/api/#custom-filters – how to write a custom filter