Flask – a microframework

To get a taste of working with a Python web framework, we're going to write a small app with Flask. We've chosen Flask because it provides a lean interface, giving us the features we need while getting out of the way and letting us code. Also, it doesn't require any significant preconfiguration, all we need to do is install it, like this:

>>> pip install flask
Downloading/unpacking flask

Flask can also be downloaded from the project's homepage at http://flask.pocoo.org. Note that to run Flask under Python 3, you will need Python 3.3 or higher.

Now create a project directory, and within the directory create a text file called tinyflaskapp.py. Our app is going to allow us to browse the docstrings for the Python built-in functions. Enter this into tinyflaskapp.py:

from flask import Flask, abort
app = Flask(__name__)
app.debug = True

objs = __builtins__.__dict__.items()
docstrings = {name.lower(): obj.__doc__ for name, obj in objs if
              name[0].islower() and hasattr(obj, '__name__')}

@app.route('/')
def index():
    link_template = '<a href="/functions/{}">{}</a></br>'
    links = []
    for func in sorted(docstrings):
        link = link_template.format(func, func)
        links.append(link)
    links_output = '
'.join(links)
    return '<h1>Python builtins docstrings</h1>
' + links_output

@app.route('/functions/<func_name>')
def show_docstring(func_name):
    func_name = func_name.lower()
    if func_name in docstrings:
        output = '<h1>{}</h1>
'.format(func_name)
        output += '<pre>{}</pre>'.format(docstrings[func_name])
        return output
    else:
        abort(404)

if __name__ == '__main__':
    app.run()

This code can be found in this book's source code download for this chapter within the 1-init folder.

Flask includes a development web server, so to try out our application all we need to do is run the following command:

$ python3.4 tinyflaskapp.py
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
 * Restarting with stat

We can see that the Flask server tells us the IP address and port it's listening on. Connect to the URL it displays (in the preceding example this is http://127.0.0.1:5000/) now in a web browser, and you should see a page with a list of Python built-in functions. Clicking on one should display a page showing the function name and its docstring.

If you want to run the server on another interface or port, you can change the app.run() call, for example, to app.run(host='0.0.0.0', port=5001).

Let's go through our code. From the top, we create our Flask app by creating a Flask instance, in this case giving it the name of our main module. We then set debug mode to active, which provides nice tracebacks in the browser when something goes wrong, and also sets the development server to automatically reload code changes without needing a restart. Note that debug mode should never be left active in a production app! This is because the debugger has an interactive element, which allows code to be executed on the server. By default, debug is off, so all we need to do is delete the app.config.debug line when we put the app into production.

Next we filter the built-in function objects out of the globals and extract their docstrings for later use. Now we have the main section of the app, and we encounter the first of Flask's superpowers: URL routing. The heart of a Flask app is a set of functions, usually called views, that handle requests for various parts of our URL space—index() and show_docstring() are such functions. You will see both are preceded by a Flask decorator function, app.route(). This tells Flask which parts of our URL space the decorated function should handle. That is, when a request comes in with a URL that matches a pattern in an app.route() decorator, the function with the matching decorator is called to handle the request. View functions must return a response that Flask can return to the client, but more on that in a moment.

The URL pattern for our index() function is just the site root, '/', meaning that only requests for the root will be handled by index().

In index(), we just compile our output HTML as a string—first our list of links to the functions' pages, then a header—and then we return the string. Flask takes the string and creates a response out of it, using the string as the response body and adding a few HTTP headers. In particular, for str return values, it sets Content-Type to text/html.

The show_docstrings() view does a similar thing—it returns the name of the built-in function we're viewing in an HTML header tag, plus the docstring wrapped in a <pre> tag (to preserve new lines and whitespace).

The interesting part is the app.route('/functions/<func_name>') call. Here we're declaring that our functions' pages will live in the functions directory, and we're capturing the name of the requested function using the <func_name> segment. Flask captures the section of the URL in angle brackets and makes it available to our view. We pull it into the view namespace by declaring the func_name argument for show_docstring().

In the view, we check that the name supplied is valid by seeing whether it appears in the docstrings dict. If it's okay, we build and return the corresponding HTML. If it's not okay, then we return a 404 Not Found response to the client by calling Flask's abort() function. This function raises a Flask HTTPException, which if not handled by our application, will cause Flask to generate an error page and return it to the client with the corresponding status code (in this case 404). This is a good way to fail fast when we encounter bad requests.

Templating

You can see from our preceding views that even when cheekily omitting the usual HTML formalities such as <DOCTYPE> and the <html> tag to save complexity, constructing HTML in Python code is clunky. It's difficult to get a feel for the overall page, and it's impossible for designers with no Python knowledge to work on the page design. Also, mixing the generation of the presentation code with the application logic makes both harder to test.

Pretty much all web frameworks solve this problem by employing the template idiom. Since the bulk of the HTML is static, the question arises: Why keep it in the application code at all? With templates, we extract the HTML entirely into separate files. These then comprise HTML code, with the inclusion of some special placeholder and logic markup to allow dynamic elements to be inserted.

Flask uses another Armin Ronacher creation, the Jinja2 templating engine, for this task. Let's adapt our application to use templates. In your project folder, create a folder called templates. In there, create three new text files, base.html, index.html, and docstring.html. Fill them out as follows:

The base.html file will be like this:

<!DOCTYPE html>
<html>
<head>
    <title>Python Builtins Docstrings</title>
</head>
<body>
{% block body %}{% endblock %}
</body>
</html>

The index.html file will be like this:

{% extends "base.html" %}
{% block body %}
    <h1>Python Builtins Docstrings</h1>
    <div>
    {% for func in funcs %}
        <div class="menuitem link">
            <a href="/functions/{{ func }}">{{ func }}</a>
        </div>
    {% endfor %}
    </table>
{% endblock %}

The docstring.html file will be like this:

{% extends 'base.html' %}
{% block body %}
    <h1>{{ func_name }}</h1>
    <pre>{{ doc }}</pre>
    <p><a href="/">Home</a></p>
{% endblock %}

Add render_template to the from flask import... line at the top of tinyflaskapp.py, then modify your views to look like this:

@app.route('/')
def index():
    return render_template('index.html', funcs=sorted(docstrings))

@app.route('/functions/<func_name>')
def show_docstring(func_name):
    func_name = func_name.lower()
    if func_name in docstrings:
        return render_template('docstring.html',
                               func_name=func_name,
                               doc=docstrings[func_name])
    else:
        abort(404)

This code can be found in the 2-templates folder of this chapter's source code.

Notice how the views become much simpler, and the HTML is much more readable now? Instead of composing a return string by hand, our views simply call render_template() and return the result.

So what does render_template() do? Well, it looks in the templates folder for the file supplied as the first argument, reads it, runs any processing instructions in the file, then returns the processed HTML as a string. Any keyword arguments supplied to render_template() are passed to the template and become available to its processing instructions.

Looking at the templates, we can see they are mostly HTML, but with some extra instructions for Flask, contained in {{ }} and {% %} tags. The {{ }} instructions simply substitute the value of the named variable into that point of the HTML. So for example the {{ func_name }} in docstrings.html substitutes the value of the func_name value we passed to render_template().

The {% %} instructions contain logic and flow control. For example, the {% for func in funcs %} instruction in index.html loops over values in funcs and repeats the contained HTML for each value.

Finally, you may have spotted that templates allow inheritance. This is provided by the {% block %} and {% extends %} instructions. In base.html we declare some shared boilerplate HTML, then in the <body> tag we just have a {% block body %} instruction. In index.html and docstring.html, we don't include the boilerplate HTML; instead we extend base.html, meaning that these templates will fill the block instructions declared in base.html. In both index.html and docstring.html, we declare a body block, the contents of which Flask inserts into the HTML in base.html, replacing the matching {% block body %} there. Inheritance allows the reuse of common code, and it can cascade through as many levels as needed.

There is a lot more functionality available in Jinja2 template instructions; check out the template designer documentation for a full list at http://jinja.pocoo.org/docs/dev/templates/.

Other templating engines

Jinja2 is certainly not the only templating package in existence; you can find a maintained list of Python templating engines at https://wiki.python.org/moin/Templating.

Like frameworks, different engines exist because of differing philosophies on what makes a good engine. Some feel that logic and presentation should be absolutely separate and that flow control and expressions should never be available in templates, providing only value substitution mechanisms. Others take the opposite tack and allow full Python expressions within template markup. Others, such as Jinja2, take a middleground approach. And some engines use different schemes altogether, such as XML-based templates or declaring logic via special HTML tag attributes.

There isn't a "right" approach; it's best to experiment with a few and see what works best for you. Where a framework has its own engine though, like Django, or is tightly integrated with an existing engine, like Flask, you'll usually have a smoother run sticking with what they supply, if you can.

Adding some style

At the moment, our pages look a little plain. Let's add some style. We'll do this by including a static CSS document, but the same approach can be used to include images and other static content. The code for this section can be found in the 3-style folder in this chapter's source code.

First create a new static folder in your project folder, and in there create a new text file called style.css. Save the following to it:

body        { font-family: Sans-Serif; background: white; }
h1          { color: #38b; }
pre         { margin: 0px; font-size: 1.2em; }
.menuitem   { float: left; margin: 1px 1px 0px 0px; }
.link       { width: 100px; padding: 5px 25px; background: #eee; }
.link a      { text-decoration: none; color: #555; }
.link a:hover { font-weight: bold; color: #38b; }

Next update the <head> section of your base.html file to look like this:

<head>
    <title>Python Builtins Docstrings</title>
    <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}"/>
</head>

Note the third and forth lines in the preceding code—that is the <link> tag—should be a single line in your code. Try your web application in the browser again and notice that it looks (hopefully) a little more up to date.

Here we've just added a stylesheet to our boilerplate HTML in base.html, adding a <link> tag pointing to our static/style.css file. We use Flask's url_for() function for this. The url_for() function returns paths to named parts of our URL space. In this case, it's the special static folder, which by default Flask looks for in the root of our web application. Another thing we can use url_for() for is to get the paths of our view functions, for example, url_for('index') would return /.

You can put images and other resources in the static folder, and reference them in the same way.

A note on security

If you're new to web programming, then I strongly recommend you read up on two common types of security flaw in web applications. Both are fairly easily avoided but can have serious consequences if not addressed.

XSS

The first is Cross-Site Scripting (XSS). This is where an attacker injects malicious script code into a site's HTML, causing a user's browser to carry out operations in the security context of that site without the user's knowledge. A typical vector is user submitted info being redisplayed to users without proper sanitization or escaping.

For example, one method is to trick users into visiting URLs containing carefully crafted GET parameters. As we saw in Chapter 2, HTTP and Working with the Web, these parameters can be used by web servers to generate pages, and sometimes their content is included in the HTML of the response page itself. If the server is not careful to replace special characters in the URL parameters with their HTML escape codes when displayed, an attacker can put executable code, for example Javascript, into URL parameters and actually have it executed when that URL is visited. If they can trick a victim into visiting that URL, that code will be executed in the user's browser, enabling the attacker to potentially perform any action the user could.

The basic XSS prevention is to ensure that any input received from outside the web application is escaped properly when returned to the client. Flask is very helpful in this regard since it activates Jinja2's auto-escaping feature by default, meaning that anything we render via template is automatically protected. Not all frameworks have this feature though, and some that do need it to be manually set. Also, this only applies in situations where your user-generated content can't include markup. In situations like a wiki that allows some markup in user-generated content, you need to take much greater care—see the source code download for this chapter in the 5-search folder for an example of this. You should always make sure you check out your framework's documentation.

CSRF

The second form of attack is the Cross-Site Request Forgery (CSRF). In this attack, a site is tricked into carrying out actions in the security context of a user, without the user's knowledge or consent. Frequently this is initiated by an XSS attack that causes a user's browser to perform an operation on the target site while the user is logged in. It should be noted that this can affect sites even when a user isn't actively browsing them; sites often clear cookie authentication tokens only when a user explicitly logs out, and hence from the site and browser's point of view, any request coming from the browser even after the user has stopped browsing a site—if they haven't logged out—will be as if the user is still logged in.

One technique to help prevent CSRF attacks is to make potentially abusable operations, such as submitting forms, require a one-time nonce value that is only known to the server and the client. CRSF attacks often take the form of a pre-composed HTTP request, mimicking a user submitting a form or similar. However, if every time a server sends a form to a client it includes a different nonce value, then the attacker has no way of including this in the pre-composed request, and hence the attack attempt can be detected and rejected. This technique is less effective against XSS initiated attacks, and attacks where an attacker is eavesdropping the HTTP traffic of a browsing session. The former is difficult to completely protect against, and the best solution is to ensure XSS vulnerabilities are not present in the first place. The latter can be mitigated using HTTPS rather than HTTP. See the OWASP pages linked to below for further information.

Different frameworks have different approaches to providing nonce-based CSRF protection. Flask doesn't have this functionality built in, but it is very easy to add something, for example:

@app.before_request
def csrf_protect():
    if request.method == "POST":
        token = session.pop('_csrf_token', None)
        if not token or token != request.form.get('_csrf_token'):
            abort(403)

def generate_csrf_token():
    if '_csrf_token' not in session:
        session['_csrf_token'] = some_random_string()
    return session['_csrf_token']

app.jinja_env.globals['csrf_token'] = generate_csrf_token

Then in templates with forms, just do the following:

<form method="post" action="<whatever>">
    <input name="_csrf_token" type="hidden" value="{{ csrf_token() }}">

This is from the Flask site: http://flask.pocoo.org/snippets/3/. Although this contains some Flask functionality, we haven't covered, including sessions and the @app.before_request() decorator, you just need to include the above code in your app, and make sure you include a _ csrf_token hidden input in every form. An alternative approach is to use the Flask-WTF plugin that provides integration with the WTForms package, which has built-in CSRF protection.

Django on the other hand has built-in protection, though you need to enable and use it. Other frameworks vary. Always check your chosen framework's documentation.

Note

There is more information on XSS and CSRF on the Flask and Django sites:

Also on the OWASP site, there is a repository of all sorts of computer security related information:

Finishing up with frameworks

That's as far as we're going to take our dip into Flask, here. There are some examples of further adaptations to our application in the downloadable source code of this chapter, notably form submission, accessing form values in the request, and sessions. The Flask tutorial covers many of these elements in some detail, and is well worth checking out http://flask.pocoo.org/docs/0.10/tutorial/.

So that's a taste of what a very basic Python web application can look like. There are obviously as many ways to write the same app as there are frameworks though, so how do you choose a framework?

Firstly, it helps to have a clear idea of what you're looking to achieve with your application. Do you require database interaction? If so, a more integrated solution like Django may be quicker to get started with. Will you need a web-based data entry or administration interface? Again if so, Django has this out of the box.

Next you can look at your environment. Are there already preferred packages in your organization for operations you might want to perform, such as database access or unit testing? If so, do any frameworks already use these? If not then a microframework might be a better option, plugging in your required packages. Do you have a preferred operating system or web server for hosting, and which frameworks support these? Does your hosting restrict you in terms of Python version, database technology, or similar? Also, if you have web designers, do you have time to get them up to speed on a complex templating language, or must it be kept simple?

Answers to these questions can help you narrow down your choices. Then, researching the frameworks, asking people who are using them, and trying out a few likely looking ones will get you where you need to go.

Having said that, for a general web application that needs some user form submission and database access, you can't really go wrong with Django. It really is "batteries included", its database model is elegant, and its out-of-the box database administration and data entry interface is very powerful and can be a huge timesaver. And for simpler applications such as APIs, Flask is also a great choice, coupled with SQLAlchemy if database access is needed.

As I mentioned before, there's no right answer, but there's a lot to be learned by exploring what's available and seeing the different approaches that the frameworks take.

Of course, once we've got our web application, we need a way to host it. We're going to look at some options now.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.218.157.34