Welcome to part 28 of our Flask web development tutorial series. In this tutorial, we're going to be discussing custom converters. The idea of these custom converters is to allow us to create very dynamic URLs, where part of the URL is actually treated as a variable.
An example of where this may prove useful might be where you have something with pagination, or maybe a user account page. Another example would be with PythonProgramming.net's development version content management system, where we have lots of pages that are almost identical, where the only main difference in the function itself is the URL and the template that is rendered, along with some minor variable changes along the way. Let's start with a simple example of a converter:
__init__.py
@app.route('/converters/<page>/') def convertersexample(page): try: return render_template("converterexample.html", page=page) except Exception, e: return(str(e))
Here, you can see that we use a tag of sorts, for page
in the actual URL mapping. Then, in the function, we pass page
as a parameter. Next up, we need a template to make use of, so let's just make one real quick:
converterexample.html
{% extends "header.html" %} {% block body %} <body class="body"> <div class="container"> <p>{{page}}</p> </div> </body> {% endblock %}
With that, we're seeing the actual page is being passed through, should we visit the URL like: /converters/1/
.
What if we wanted to not require page 1 to have the 1 in the URL? If we are to visit /converters/ for example, we're going to 404 as it stands now. The first stop might be to add another URL like so:
@app.route('/converters/') @app.route('/converters/<page>/') def convertersexample(page): try: return render_template("converterexample.html", page=page) except Exception, e: return(str(e))
This, however, returns an error, because we're still wanting page! Luckily, we can resolve this with setting a default to the page parameter like so:
@app.route('/converters/') @app.route('/converters/<page>/') def convertersexample(page=1): try: return render_template("converterexample.html", page=page) except Exception, e: return(str(e))
That should be enough to get you wherever you need to go, but there are a few other additions that you can make. For example, you can treat/require the paths to be datatypes, like integers, floats or strings. You can also make the parameter a path itself, so you can actually include slashes within the parameter. Let's see some examples here.
@app.route('/converters/') @app.route('/converters/<int:page>/') def convertersexample(page=1): try: return render_template("converterexample.html", page=page) except Exception, e: return(str(e))
The code above will automatically treat the page parameter as an integer. Not only this, but the outcome here is that it *must* be a convert-able integer. Before, we could have visited /converters/hi/
, but "hi" cannot be an integer. If you visit that URL now, you will get an error!
Another failure would be: /converters/5.00/
. That's an integer, but the period is wreaking havoc! No problem, we can say we're expecting a float:
@app.route('/converters/') @app.route('/converters/<float:page>/') def convertersexample(page=1): try: return render_template("converterexample.html", page=page) except Exception, e: return(str(e))
Awesome, now /converters/5.00/
works just fine! Another option is string. Initially, you may think a string is just like the others, but that is where you are wrong! You can pass a period, and even some symbols.
@app.route('/converters/') @app.route('/converters/<string:page>/') def convertersexample(page=1): try: return render_template("converterexample.html", page=page) except Exception, e: return(str(e))
Now visit /converters/Hey there, what a nice day it is today. Python Flask is great!@$^
. That should work, and you should see the text being displayed back to you. Very nifty. What if you want multiple converters? You have a few options. One could be to do something like:
__init__.py
@app.route('/converters/') @app.route('/converters/<string:article>/<int:page>') def convertersexample(article, page=1): try: return render_template("converterexample.html", page=page, article=article) except Exception, e: return(str(e))
templates/converterexample.html
{% extends "header.html" %} {% block body %} <body class="body"> <div class="container"> <p>{{page}}</p> <p>{{article}}</p> </div> </body> {% endblock %}
Now, we can visit /converters/Hello World!/1
, where Hello World! is the title and 1 is the page.
That is one option. What if, however, we might have a path with maybe 2 elements... or maybe a path with 15? What I am about to show you...is capable of allowing you to have even a large website all contained within one function! Prepare to be amazed!
__init__.py
@app.route('/converters/') @app.route('/converters/<path:urlpath>') def convertersexample(urlpath): try: return render_template("converterexample.html", urlpath=urlpath) except Exception, e: return(str(e))
converterexample.html
{% extends "header.html" %} {% block body %} <body class="body"> <div class="container"> <p>{{urlpath}}</p> </div> </body> {% endblock %}
Now we can visit /converters/hi/there/page5
, and see everything came through, including our slashes. Now... what if...we go to our main functions that corresponds to our homepage? Can we get away with:
@app.route('/<path:urlpath>', methods=['GET', 'POST']) @app.route('/', methods=['GET', 'POST']) def main(urlpath='/'):
We sure can! Just don't forget to allow for the "no path" option and the default just in case. So here, you could actually contain a fairly advanced website, all within one function. This doesn't mean you actually should do that, but this shows you the power of these URL converters. Should all this not be enough for you, you can also create your very own custom converters.
That's all on the converters. Next up, we're going to be discussing the Flask-Email module, which is a very powerful emailing aparatus that works very well with Flask.