Introduction to Flask python framework

Tue August 11 2015

Jhon Jairo Roa

Full Stack Developer and Tech Lead

Co-Founder and CTO @ Trotter

Flask

Flask

micro web development framework for python

microframework? 

Yes, microframework 

is NOT a full stack framework like Django

it covers basic web development stuff

Web server and debugger

Testing

Routing

Templating (jinja2)

Request data access

 

is based on two external libraries:

 Jinja2: python template library 

Werkzeug: HTTP utility library for Python.

Getting started

Getting started

from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello World!"

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

Getting started

$ pip install Flask
$ python hello.py
 * Running on http://localhost:5000/

Debugger

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello World!'

if __name__ == '__main__':
    app.run()
+
++
app.run(debug=True)
from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello World!'

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

Testing

Testing Skeleton

import os
import flaskr
import unittest
import tempfile

class FlaskrTestCase(unittest.TestCase):

    def setUp(self):
        self.db_fd, flaskr.app.config['DATABASE'] = tempfile.mkstemp()
        flaskr.app.config['TESTING'] = True
        self.app = flaskr.app.test_client()
        flaskr.init_db()

    def tearDown(self):
        os.close(self.db_fd)
        os.unlink(flaskr.app.config['DATABASE'])

if __name__ == '__main__':
    unittest.main()

Testing Skeleton

import os
import flaskr
import unittest
import tempfile

class FlaskrTestCase(unittest.TestCase):

    def setUp(self):
        self.db_fd, flaskr.app.config['DATABASE'] = tempfile.mkstemp()
        flaskr.app.config['TESTING'] = True
        self.app = flaskr.app.test_client()
        flaskr.init_db()

    def tearDown(self):
        os.close(self.db_fd)
        os.unlink(flaskr.app.config['DATABASE'])

if __name__ == '__main__':
    unittest.main()
$ python flaskr_tests.py

----------------------------------------------------------------------
Ran 0 tests in 0.000s

OK

Testing a feature

import os
import flaskr
import unittest
import tempfile

class FlaskrTestCase(unittest.TestCase):

    ...
    ...
    ...

    def login(self, username, password):
        return self.app.post('/login', data=dict(
            username=username,
            password=password
        ), follow_redirects=True)

    def logout(self):
        return self.app.get('/logout', follow_redirects=True)

    ...
    ...
    ...

if __name__ == '__main__':
    unittest.main()

Testing a feature

import os
import flaskr
import unittest
import tempfile

class FlaskrTestCase(unittest.TestCase):

    ...
    ...
    ...

    def login(self, username, password):
        return self.app.post('/login', data=dict(
            username=username,
            password=password
        ), follow_redirects=True)

    def logout(self):
        return self.app.get('/logout', follow_redirects=True)

    def test_login_logout(self):
        rv = self.login('admin', 'default')
        assert 'You were logged in' in rv.data
        rv = self.logout()
        assert 'You were logged out' in rv.data
        rv = self.login('adminx', 'default')
        assert 'Invalid username' in rv.data
        rv = self.login('admin', 'defaultx')
        assert 'Invalid password' in rv.data

    ...
    ...
    ...

if __name__ == '__main__':
    unittest.main()

Testing a feature

import os
import flaskr
import unittest
import tempfile

class FlaskrTestCase(unittest.TestCase):

    ...
    ...
    ...

    def login(self, username, password):
        return self.app.post('/login', data=dict(
            username=username,
            password=password
        ), follow_redirects=True)

    def logout(self):
        return self.app.get('/logout', follow_redirects=True)

    def test_login_logout(self):
        rv = self.login('admin', 'default')
        assert 'You were logged in' in rv.data
        rv = self.logout()
        assert 'You were logged out' in rv.data
        rv = self.login('adminx', 'default')
        assert 'Invalid username' in rv.data
        rv = self.login('admin', 'defaultx')
        assert 'Invalid password' in rv.data

    ...
    ...
    ...

if __name__ == '__main__':
    unittest.main()

Testing a feature

$ python flaskr_tests.py
...
------------------------------------
Ran 2 tests in 0.332s

OK

Routing

Routing

@app.route('/')
def index():
    return 'Index Page'

@app.route('/hello')
def hello():
    return 'Hello World'

Routing: Variable rules

@app.route('/user/<username>')
def show_user_profile(username):
    # show the user profile for that user
    return 'User %s' % username

@app.route('/post/<int:post_id>')
def show_post(post_id):
    # show the post with the given id, the id is an integer
    return 'Post %d' % post_id

Routing: Http methods

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        do_the_login()
    else:
        show_the_login_form()

Templating

Templating

<!doctype html>
<title>Hello from Flask</title>
{% if name %}
  <h1>Hello {{ name }}!</h1>
{% else %}
  <h1>Hello World!</h1>
{% endif %}
/application.py
/templates
    /hello.html

Application folder

templates/hello.html

from flask import render_template

...
...
...

@app.route('/hello/')
@app.route('/hello/<name>')
def hello(name=None):
    return render_template('hello.html', name=name)

...
...
...

application.py

Templating

<!doctype html>
<title>Hello from Flask</title>
{% if name %}
  <h1>Hello {{ name }}!</h1>
{% else %}
  <h1>Hello World!</h1>
{% endif %}

100% Jinja2

Request Data Access

@app.route('/login', methods=['POST', 'GET'])
def login():
    error = None
    if request.method == 'POST':
        if valid_login(request.form['username'],
                       request.form['password']):
            return log_the_user_in(request.form['username'])
        else:
            error = 'Invalid username/password'
    # the code below is executed if the request method
    # was GET or the credentials were invalid
    return render_template('login.html', error=error)

Responses

from flask import abort, redirect, url_for

@app.route('/')
def index():
    return redirect(url_for('login'))

@app.route('/login')
def login():
    abort(401)
    this_is_never_executed()
@app.errorhandler(404)
def not_found(error):
    resp = make_response(render_template('error.html'), 404)
    resp.headers['X-Something'] = 'A value'
    return resp

A little bit more...

Cookie handling

Session handling

Logging

Simple Flask application

Browser:

js, html ,css

Python WebServer

using Flask

Request

Response

Browser:

js, html ,css

Python WebServer

using Flask

Request

Response

1. User puts http://0.0.0.0:80/ in the browser

2. Flask returns content in index.html

# This route will show a form to perform an AJAX request
# jQuery is loaded to execute the request and update the
# value of the operation
@app.route('/')
def index():
    return render_template('index.html')

Browser:

js, html ,css

Python WebServer

using Flask

Request

Response

3. Content is received and shown

Browser:

js, html ,css

Python WebServer

using Flask

Request

Response

4. User fills form and clicks "calculate server"

Browser:

js, html ,css

Python WebServer

using Flask

Request

Response

5. Browser makes an ajax call to http://0.0.0.0:80/add/2/4

$(function() {
      $('a#calculate').bind('click', function() {
        var a = $('input[name="a"]').val();
        var b = $('input[name="b"]').val();
        $.getJSON('/add/' + a + '/' + b, 
                  {},
                  function(data) {
                    $("#result").text(data.result);
                  });
        return false;
      });
    });

Browser:

js, html ,css

Python WebServer

using Flask

Request

Response

6. Webserver performs the addition of the number and return the result

@app.route('/add/<int:a>/<int:b>')
def add_numbers(a,b):
    #a = request.args.get('a', 0, type=int)
    #b = request.args.get('b', 0, type=int)
    return jsonify(result=a + b)

Browser:

js, html ,css

Python WebServer

using Flask

Request

Response

6. The browser receives the data and shows it in the screen

couldn't be simpler :)

Let's see the source code

Application folder

app.py

from flask import Flask, render_template, request, jsonify

# Initialize the Flask application
app = Flask(__name__)

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/add/<int:a>/<int:b>')
def add_numbers(a,b):
    #a = request.args.get('a', 0, type=int)
    #b = request.args.get('b', 0, type=int)
    return jsonify(result=a + b)

if __name__ == '__main__':
    app.run(
        host="0.0.0.0",
        port=int("80"),
        debug=True
    )

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
    <link href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css"
          rel="stylesheet">
  <script type=text/javascript>
    ...
    ...
  </script>
  </head>
  <body>
    <div class="container">
      <div class="header">
        <h3 class="text-muted">How To Manage JSON Requests</h3>
      </div>
      <hr/>
      <div>
      <p>
    <input type="text" size="5" name="a"> +
    <input type="text" size="5" name="b"> =
    <span id="result">?</span>
    <p><a href="javascript:void();" id="calculate">calculate server side</a>
      </form>
      </div>
    </div>
  </body>
</html>

index.html

...

  <script type=text/javascript>
    $(function() {
      $('a#calculate').bind('click', function() {
        var a = $('input[name="a"]').val();
        var b = $('input[name="b"]').val();
        $.getJSON('/add/' + a + '/' + b, 
                  {},
                  function(data) {
                    $("#result").text(data.result);
                  });
        return false;
      });
    });
  </script>
...

References

Social

Jhon Jairo Roa

@jhonjairoroa87

 

www.linkedin.com/in/jhonjairoroa87

 

skype: jhonjairoroa87

Thanks!

Made with Slides.com