Python Flask custom view filter

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())
(Apologies for the formatting; doing code in WordPress is very hit and miss)

 

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.

 

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.