I was writing a web-based MP3 Player with on the front-end while using Flask on the back-end. amplitudejs.com To navigate a large MP3 collection, I placed buttons everywhere - picking out all the fields like genres, year, artist, and album which my database of MP3s supported. Buttons for genre and year (high-level filters) are placed permanently in a sidebar on the left. Buttons for artist and album are shown against a list of matching MP3 albums on the right. we want query strings to work in concert Just to give you an idea of what I was trying to achieve, the front end of the app looks like this--using buttons to filter the MP3 collection. Clicking will replace the list of from the list on the right with indie bands; replace the tracklist and highlight the button. indie songwriters indie Clicking will replace the list of with songwriters from the 90s... and so on. 90s 80s songwriters But it doesn't really matter about any of that. The point is we want query strings to work in concert in a Flask app, in useful ways, to add filters for page results - in this case, to filter the playlist available to the amplitudejs MP3 player in the middle of the app's page. A Scenario For instance, let's assume the current query string of the URL is domain.com/music/?year=1980&genre=rock This would imply I have already clicked the year and genre filters. As an example, this is how I would like the URLs for all the filter buttons to resolve. Year group filters. The and buttons change the year query string, the button toggles it off. 1979 1981 1980 => => href=/music/?year=1980&genre=rock 1979 href=/music/?year=1979&genre=rock => => href=/music/?year=1980&genre=rock 1980 href=/music/?genre=rock => => href=/music/?year=1980&genre=rock 1981 href=/music/?year=1981&genre=rock Genre group filters. The and filters change the genre query string, the button toggles it off. jazz pop rock => => href=/music/?year=1980&genre=rock jazz href=/music/?year=1980&genre=jazz => => href=/music/?year=1980&genre=rock pop href=/music/?year=1980&genre=pop => => href=/music/?year=1980&genre=rock rock href=/music/?year=1980 Artist filters. The and buttons just add to the existing query string. sting genesis => => href=/music/?year=1980&genre=rock sting href=/music/?year=1980&genre=jazz&artist=sting => => href=/music/?year=1980&genre=rock genesis href=/music/?year=1980&genre=pop&artist=genesis With me? I hope this description of my goal will help fellow developers facing a similar use case. Jinja2 custom filters to the rescue! In my Flask app, I created a new module file and added the features I needed in the way of two custom filters. As the for custom filters explains, these are just standard python functions: documentation /repo/myproject/myapp/jinja_filters.py urllib.parse urlencode qs_set = {(filter, by)} existing_qs qs_set: existing_qs_set = set(existing_qs.items()) existing_qs_set.intersection(qs_set) == qs_set qs = {filter: by} rtn_qs = existing_qs.copy() qs_active(existing_qs, filter, by): rtn_qs.pop(filter) : rtn_qs.update(qs) urlencode(rtn_qs) from import : def qs_active (existing_qs, filter, by) """Active when identical key/value in existing query string.""" # Not active if either are empty. if not or not return False # See if the intersection of sets is the same. return : def qs_toggler (existing_qs, filter, by) """Resolve filter against an existing query string.""" # Don't change the currently rendering existing query string! # Test for identical key and value in existing query string. if # Remove so that buttons toggle their own value on and off. else # Update or add the query string. return We will use the filter to add the class to any button whose filter values are already (duh!) active in a URL. qs_active active We will use the filter to resolve a button's filter values against the current query string. This also calls the function, reusing it. qs_toggler qs_active I wrote tests for both these functions, which will help you understand how they work. /repo/myproject/myapp/tests/test_jinja_filters.py unittest psalms.jinja_filters qs_active, qs_toggler TEST_QS = { : , : } self.assertEqual( , qs_toggler(TEST_QS.copy(), , )) self.assertEqual( , qs_toggler(TEST_QS.copy(), , )) self.assertEqual( , qs_toggler(TEST_QS.copy(), , )) self.assertEqual( , qs_toggler({}, , )) TEST_QS = { : , : } self.assertTrue(qs_active(TEST_QS, , )) self.assertTrue(qs_active(TEST_QS, , )) self.assertFalse(qs_active(TEST_QS, , )) self.assertFalse(qs_active(TEST_QS, , )) self.assertFalse(qs_active(TEST_QS, , )) self.assertFalse(qs_active({}, , )) self.assertFalse(qs_active({}, , )) self.assertFalse(qs_active({ : }, , )) import from import : class TestMp3Model (unittest.TestCase) : def test_jinja_filters_qs_toggler (self) "a" "1" "b" "2" # Adds a new qs "a=1&b=2&c=3" "c" "3" # Changes an existing qs "a=1&b=1" "b" "1" # Removes a qs of the same value. "b=2" "a" "1" # Handle empty existing qs "a=1" "a" "1" : def test_jinja_filters_qs_active (self) "a" "1" "b" "2" "a" "1" "b" "2" "a" "2" "b" "1" "c" "3" # Handles empty query strings "a" "1" "" "" "a" "1" "" "" Coupling the custom filters to the Jinja2 templates I opened my main Flask app page, imported these filters, then told Flask I wanted to use them as custom template : filters __init__.py flask myapp.views Main, Music myapp.jinja_filters qs_active, qs_toggler app = flask.Flask(__name__) app.jinja_env.filters[ ] = qs_active app.jinja_env.filters[ ] = qs_toggler app.add_url_rule( , view_func=Music.as_view( ), methods=[ ] ) import # You should already have this! # You should already have something like this for your views! from import # You'll add this to your current solution. from import # You should already have this! # You'll add this to your current solution "qs_active" "qs_toggler" # You should already have some other stuff as well, like this: "/music/" "music" "GET" Handling the query strings in a Flask view My Flask view (handling the URL) looks like this: /repo/myproject/myapp/views.py existing_qs_as_dict = request.args.to_dict() sidebar, results = navigator(existing_qs_as_dict) flask.render_template( , sidebar=sidebar, results=results, existing_qs=existing_qs_as_dict, ) : class Music (flask.views.MethodView) @login_required : def get (self) # Existing query string as a dictionary # use the existing query string to filter the db. # You'll do this your way! # Send existing query string back into the template return "music.html" I added a file instantiating a Flask macro to build the buttons. The macro calls my custom Jinja filters and decides whether the querystring key (which the buttons control) is already active and creates a link combining any other querysting keys with its own filter value. /repo/myproject/myapp/templates/macros.html {% macro filter_music_button(filter, by) %} < = a href "/music/? {{ existing_qs|qs_toggler(filter, by) }} "> < | ( , ) %} = {% %}> button{% if existing_qs qs_active filter by class "active" endif {{ by }} {% endmacro %} </ > button </ > a In the main template, for writing the buttons, you'd import the macro like this at the top of the page: /repo/myproject/myapp/templates/music.html {% from "macros.html" import filter_music_button with context %} Now you can add buttons simply by calling the macro (where appropriate), like this: {{filter_music_button("year", 1980"}} For instance, in my implementation, I send the variable to my template in the context (see above) -- a dictionary of high-level filters with lists of values to filter against, pulled out of the MP3 collection, which I iterate like this: sidebar class Music /repo/myproject/myapp/templates/music.html {% block menu %} {% for filter, bys in sidebar.items() %} < > h3 {{ filter }} {% for by in bys %} </ > h3 < > p {{ filter_music_button(filter, by) }} {% endfor %} {% endfor %} {% endblock %} </ > p Given the scenario above (where the current URL is , it would render like this: ?year=1980&genre=rock year 1979 1980 1981 genre jazz pop rock < > h3 </ > h3 < > p < = > a href "/music/?year=1979&genre=rock" < > button </ > button </ > a < = > a href "/music/?genre=rock" < = > button class "active" </ > button </ > a < = > a href "/music/?year=1981&genre=rock" < > button </ > button </ > a </ > p < > h3 </ > h3 < > p < = > a href "/music/?year=1980&genre=jazz" < > button </ > button </ > a < = > a href "/music/?year=1980&genre=pop" < > button </ > button </ > a < = > a href "/music/?year=1980" < = > button class "active" </ > button </ > a </ > p How you implement the solution exactly will largely come down to your application. Meanwhile, I hope this post has shown you how to: Create Jinja2 custom filters for handling query strings. Activate them for your Flask app. Call your custom filters in the templates. Handle the throughput of query strings from to to to to to to to to ... URL view template render button URL view template render button URL Core, thanks! https://flask.palletsprojects.com/en/1.1.x/ https://jinja.palletsprojects.com/en/2.11.x/ Photo by on Daniel Schludi Unsplash For those interested in playing music from a directory of MP3 files using query strings in a Flask app, this is the project which is working nicely: psalms