Quantcast
Channel: Blog – Stormpath User Identity API
Viewing all articles
Browse latest Browse all 278

Build a Flask App in 30 Minutes

$
0
0

Flask App in 30 minutes

Flask is an awesome web framework for Python. It’s minimal, it’s simple, and best of all: easy to learn.

Today I’m going to walk you through building your very first Flask web application! Just like the official Flask tutorial, you’ll be building your very own micro blog: Flaskr. Unlike the official Flask tutorial — you’ll be speeding things up by using Stormpath to create and manage user accounts and data. This will dramatically speed up the development process!

Let’s get right to it.

NOTE: This tutorial is meant for new Flask developers, to help give them an understanding of how to build a simple website with Flask and Stormpath. This is a modified version of the official Flask tutorial.

Goals

The goals of the Flaskr app are simple:

  • Let a user sign in and out of the micro blog using a previously generated user account (which will be stored by Stormpath).
  • Let a logged in user add new entries to a page consisting of a text-only title, and some HTML body text. This HTML won’t be sanitized, as this user is trusted.
  • Display all blog entries on the main page of the site, in reverse chronological order (newest on top).

The final site should look like this:

Flaskr

Prerequisites

Before continuing, we’ll need to install several Python packages so we can make things work! We’ll do this via pip — the Python package manager.

1
$ pip install flask flask-stormpath

The above command will install two packages: Flask, and Flask-Stormpath, which will both be used through this tutorial.

Next, you need to create a Stormpath account. You can do so by signing up on the Stormpath website: https://api.stormpath.com/register

Once you’ve created a Stormpath account, and logged into it, you’ll also need to create an API key. You can do this by clicking the Create API Key button on the dashboard page: https://api.stormpath.com/ui/dashboard

When you create an API key, you will be prompted to download a file named apiKey.properties. We’ll use this later.

NOTE: Do not check the apiKey.properties file into your version control system (if you’re using one)! This file holds your Stormpath credentials, so it should be kept safe.

Next, you’ll want to create a new Stormpath application by visiting the Applications page: https://api.stormpath.com/v#!applications, then clicking Register Application. Create a new application named flaskr, with the default options selected.

Lastly, visit the Accounts page: https://api.stormpath.com/ui/accounts/create, and create a new user account in: flaskr Directory. Any account created here can be used to log into the micro blog you’re about to build.

Directory Structure

The first thing you need to do is create a directory structure to hold your application code. You’ll need to generate several directories, and move your apiKey.properites file into the new project directory:

1
2
3
4
5
6
7
8
$ mkdir -p flaskr/{static,templates}; cp ~/path/to/apiKey.properties flaskr/
$ tree flaskr
flaskr
├── apiKey.properties
├── static
└── templates

2 directories, 1 file

The flaskr folder will be the root of your application. The static folder will hold all of your static assets (css, javascript, and images). The templates folder will hold your Jinja templates, which render HTML.

App Setup

Now that you’ve got the directory structure figured out, let’s configure the app!

Firstly, create a new file in your flaskr folder named flaskr.py. This is where you’ll put your application code.

Here’s what you’ll start with:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
from datetime import datetime

from flask import (
    Flask,
    abort,
    flash,
    redirect,
    render_template,
    request,
    url_for,
)
from flask.ext.stormpath import (
    StormpathError,
    StormpathManager,
    User,
    login_required,
    login_user,
    logout_user,
    user,
)


app = Flask(__name__)
app.config['DEBUG'] = True
app.config['SECRET_KEY'] = 'some_really_long_random_string_here'
app.config['STORMPATH_API_KEYFILE'] = 'apiKey.properties'
app.config['STORMPATH_APPLICATION'] = 'flaskr'

stormpath_manager = StormpathManager(app)

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

There’s a few things to note here:

  • You’re importing several libraries at the top of the flaskr.py file — these will be used down below, through the rest of the tutorial.
  • You’re creating an app object — this is the core of every Flask project.
  • You’re adding several configuration variables to app.config. app.config is a Python dictionary that allows you to store whatever custom settings you’d like. Here, we’re setting several important variables for usage later on:
    • The DEBUG variable can be set to True or False. This controls Flask’s built in error reporting behavior (it makes Flask display verbose error messages while in development mode).
    • The SECRET_KEY variable is used internally to help keep client-side sessions secure. Make sure this is a long, randomly generated string when deploying a real Flask app.
    • The STORMPATH_API_KEYFILE variable should point to the location of your apiKey.properties file. For more advanced information, see: http://flask-stormpath.readthedocs.org/en/latest/#step-1-create-a-stormpathmanager
    • The STORMPATH_APPLICATION variable should be the name of your Stormpath application, that you created previously.
  • You’re creating a stormpath_manager object. This controls the Stormpath library, and allows you to easily interact with users and user data later on.
  • You’re calling app.run() at the bottom. This tells Flask to run your site in development mode for testing.

If you now run the following, you’ll see your Flask app start running on port 5000:

1
2
3
$ python flaskr.py
 * Running on http://127.0.0.1:5000/
 * Restarting with reloader

If you visit http://127.0.0.1:5000, however, you’ll get a 404 not found message. This is because we haven’t yet defined any views or URL routes!

Views

Now that you’ve got the setup part finished, let’s define the views. This code should go inside the flaskr.py file, above the:

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

Bit.

Here’s the code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
@app.route('/')
def show_posts():
    posts = []
    for account in stormpath_manager.application.accounts:
        if account.custom_data.get('posts'):
            posts.extend(account.custom_data['posts'])

    posts = sorted(posts, key=lambda k: k['date'], reverse=True)
    return render_template('show_posts.html', posts=posts)


@app.route('/add', methods=['POST'])
@login_required
def add_post():
    if not user.custom_data.get('posts'):
        user.custom_data['posts'] = []

    user.custom_data['posts'].append({
        'date': datetime.utcnow().isoformat(),
        'title': request.form['title'],
        'text': request.form['text'],
    })
    user.save()

    flash('New post successfully added.')
    return redirect(url_for('show_posts'))


@app.route('/login', methods=['GET', 'POST'])
def login():
    error = None

    if request.method == 'POST':
        try:
            _user = User.from_login(
                request.form['email'],
                request.form['password'],
            )
            login_user(_user, remember=True)
            flash('You were logged in.')

            return redirect(url_for('show_posts'))
        except StormpathError, err:
            error = err.message

    return render_template('login.html', error=error)


@app.route('/logout')
def logout():
    logout_user()
    flash('You were logged out.')

    return redirect(url_for('show_posts'))

Let’s discuss the code above.

You’ll notice the first function defined is show_posts. This function is what displays blog posts on the front page of the site. As you might have guessed, the decorator, @app.route('/'), is what tells Flask how to run this function.

Each time a user requests the URL /, Flask will run the show_posts function and return the output to the user.

The show_posts function works simply:

  • It iterates over all user accounts, looking for posts. Each post is a simple Python dictionary of the form:
1
2
3
4
5
{
    'date': '2014-04-01T22:50:49.762475',
    'text': 'Blog content.',
    'title': 'Post title'
}
  • If a post is found, it is added to the posts array.
  • It sort the posts array by date, so that newest posts are in front.
  • It renders an HTML template named show_posts.html, passing in the posts array as input.

The add_posts view allows logged in users to add new posts to the site. This view introduces several new things:

  • The @app.route(‘/add', methods=[‘POST']) decorator tells Flask to only allow HTTP POST requests on the given URL. By default, Flask only allows GET requests.
  • The @login_required decorator ensures users are logged in before allowing them access to this view. If a user isn’t logged in and tries POST’ing to the view, they’ll get an HTTP 401 UNAUTHORIZED response.
  • Any view decorated with the @login_required decorator can access the user variable. This is an object that holds the user’s account details.

The way it works is simple:

  • It checks to see if this user has any posts stored in their account yet. This is done by checking to see if user.custom_data.get('posts') is not False. user.custom_data is a Python dictionary that allows you to store any data you’d like into this user’s account.
  • It grabs the title and text fields from the POST request, and creates a new post object in the user’s posts array.
  • It saves this new post to Stormpath on this user’s account.
  • It flashes a message to be displayed to the user later on.
  • Lastly, it redirects the user to the show_posts view so the newly added post can be displayed.
  • The login and logout views are especially simple.

The login view simply grabs an email address and password from the user’s POST request, then attempts to log the user in by grabbing the user object from Stormpath, and creating a local session.

The logout view just destroys the user’s session.

Templates

The next thing that needs to be added is the template code. Flask uses the Jinja template language, which makes writing HTML templates very easy.

Let’s start by defining a layout template, templates/layout.html. This base template will be the parent of all the other templates we write. This strategy is often useful, as it allows you to define a good amount of boilerplate template code in a single place.

Add the following code to your layout.html template file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!doctype html>
<title>Flaskr</title>
<link rel=stylesheet type=text/css href="{{ url_for('static', filename='style.css') }}">
<div class=page>
  <h1>Flaskr</h1>
  <div class=metanav>
  {% if user.email %}
    <a href="{{ url_for('logout') }}">log out</a>
  {% else %}
    <a href="{{ url_for('login') }}">log in</a>
  {% endif %}
  </div>
  {% for message in get_flashed_messages() %}
    <div class=flash>{{ message }}</div>
  {% endfor %}
  {% block body %}{% endblock %}
</div>

Next is the templates/show_posts.html file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{% extends "layout.html" %}
{% block body %}
  {% if user.email %}
    <form action="{{ url_for('add_post') }}" method=post class=add-entry>
      <dl>
        <dt>Title:
        <dd><input type=text size=30 name=title>
        <dt>Text:
        <dd><textarea name=text rows=5 cols=40></textarea>
        <dd><input type=submit value=Share>
      </dl>
    </form>
  {% endif %}
  <ul class=entries>
  {% for post in posts %}
    <li><h2>{{ post['title'] }}</h2>{{ post['text']|safe }}
  {% else %}
    <li><em>Unbelievable. No posts here so far!</em>
  {% endfor %}
  </ul>
{% endblock %}

And lastly, here’s the templates/login.html template:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{% extends "layout.html" %}
{% block body %}
  <h2>Login</h2>
  {% if error %}<p class=error><strong>Error:</strong> {{ error }}{% endif %}
  <form action="{{ url_for('login') }}" method=post>
    <dl>
      <dt>Email:
      <dd><input type=text name=email>
      <dt>Password:
      <dd><input type=password name=password>
      <dd><input type=submit value=Login>
    </dl>
  </form>
{% endblock %}

The first thing to note is that the layout.html template defines a body block. This can be replaced by a block of the same name in any child template. The layout.html template displays a login or logout template, and also displays any flashed messages.

Because you’re using Flask-Stormpath, all templates have access to a magical user variable. If a user is logged in, the user’s details will be available (hence the

1
{% if user.email %}

checks).

The show_posts.html template iterates over the posts array that was passed into the render_template call in the show_posts view. Jinja allows you to loop over any iterable with the for statement.

It’s also important to note that in order to output variable contents, you need to surround the variable by squiggly braces (

1
{{ variable }}

). Since we’ve decided to allow users to input arbitrary HTML in their blog posts, we’re outputting the body of the post using the safe template filter (

1
{{ post['text']|safe }}

). By default, Jinja will automatically escape any special characters, so we need to use the safe filter to actually display any user-inputted HTML / Javascript.

Adding Style

The last thing to do is create a static/style.css file with the following contents:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
body            { font-family: sans-serif; background: #eee; }
a, h1, h2       { color: #377ba8; }
h1, h2          { font-family: 'Georgia', serif; margin: 0; }
h1              { border-bottom: 2px solid #eee; }
h2              { font-size: 1.2em; }

.page           { margin: 2em auto; width: 35em; border: 5px solid #ccc;
                  padding: 0.8em; background: white; }
.entries        { list-style: none; margin: 0; padding: 0; }
.entries li     { margin: 0.8em 1.2em; }
.entries li h2  { margin-left: -1em; }
.add-entry      { font-size: 0.9em; border-bottom: 1px solid #ccc; }
.add-entry dl   { font-weight: bold; }
.metanav        { text-align: right; font-size: 0.8em; padding: 0.3em;
                  margin-bottom: 1em; background: #fafafa; }
.flash          { background: #cee5F5; padding: 0.5em;
                  border: 1px solid #aacbe2; }
.error          { background: #f0d6d6; padding: 0.5em; }

This file is loaded by the layout.html template, and provides some decent styling.

Testing it Out

Now that we’ve gone over the application code, let’s take a look at the final product!

To run your shiny new site, you’ll want to first start up your Flask web server again by running:

1
2
3
$ python flaskr.py
 * Running on http://127.0.0.1:5000/
 * Restarting with reloader

Then visit http://127.0.0.1:5000 in your browser. You should now see the flaskr site running, and be able to log in using a Stormpath account, create posts, etc!

Below is a video of the site running — check it out :)

Any questions? Feel free to shoot us an email! We’re happy to help: support@stormpath.com


Viewing all articles
Browse latest Browse all 278

Trending Articles