You are on page 1of 58

Developing Large Web Applications Using a Micro Framework

Osvaldo Matos-Junior

once upon a time...

The story starts with an April's Fool's Joke...

How an April Fool's Joke became a Framework with Good Intentions (PyCon 2011)

Why micro?
The micro in microframework means Flask aims to keep the core simple but extensible. Flask wont make many decisions for you. (Armin Ronacher)

What's in the Box?


built in development server and debugger integrated routing system client side sessions (secure cookies) django-inspired templating language

Some Numbers
800 LOC Code 1500 LOC Tests 200 A4 Pages of Documentation over 3900 followers and 550 forks on github

a minimal application in Flask

Flask is Fun
from flask import Flask app = Flask(__name__) @app.route('/') def index(): return 'Hello World!' if __name__ == '__main__': app.run(debug=True)

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

What it looks like

debug mode

URL building

from flask import Flask, url_for app = Flask(__name__) @app.route('/login') def login(): pass @app.route('/user/<username>') def profile(username): pass with app.test_request_context(): print url_for('login') print url_for('login', next='/') print url_for('profile', username='John Doe') print url_for('static', filename='css/style.css', _external=True) /login /login?next=/ /user/John%20Doe http://localhost/static/css/style.css

rendering templates

hello.py
from flask import Flask, render_template app = Flask(__name__) @app.route('/', defaults={'name': 'Flask'}) @app.route('/<name>') def index(name): return render_template('hello.html', name=name) if __name__ == '__main__': app.run(debug=True)

templates/hello.html
<head> <title>Greetings</title> </head> <body> Hello {{ name }}! </body>

Jinja2 (Django like)


<html> <head><title>{{ title }}</title></head> <body> <ul id="navigation"> {% for item in navigation %} <li><a href="{{ item.href }}" class="{{ 'first' if loop.first }} ">{{ item.caption }}</a></li> {% endfor %} </ul> <h1>{{ title|capitalize|truncate(120) }}</h1> {{ text }} {% include "footer.html" %} </body></html>

Template Filters
<h1> {{ title|capitalize|truncate(120) }} </h1>

Built in: capitalize, lower, length, trim, striptags, truncate, urlize ... Customize: slugify, timesince ...

Macros
header FLASK
Results for: "query"
GO

300x250

Ttile for result


Long text description for the result. Long text description for the result.

ads 1 content related content

Ttile for result


Long text description for the result. Long text description for the result.

Ttile for result


Long text description for the result. Long text description for the result.

ads 2 Ttile for result footer


Long text description for the result. Long text description for the result.

Macros
{# ads.html #} {% macro google_adsense(id, width=120, height=120) -%}
<script>google_ad_client = "pub-XXXX" ; google_ad_slot = "{{id}}"; google_ad_width = "{{width}}" ; google_ad_height = "{{height}}" ;<script /> <script type="text/javascript" src="http://pagead2.googlesyndication. com/pagead/show_ads.js" ></ script>

{%- endmacro %} {# sidebar.html #} {% from 'ads.html' import ads_google %} <div>{{ google_adsense('arroba', 300, 250) }}</div> <div>{{ google_adsense('ads1', 300, 100) }}</div> {% include "related_content.html" %} <div>{{ google_adsense('ads2', 300, 100) }}</div>

header 2

Template Inheritance
header content 3

header content footer 2

content 2 footer

footer

Base Template
<html> <head> <link rel="stylesheet" href="style.css" /> <title>{% block title %}{% endblock %} - My Webpage</title> </head> <body> <div id="content">{% block content %}{% endblock %}</div> <div id="footer"> {% block footer %} &copy; Copyright 2008 by <a href="http://domain.invalid/">you</a>. {% endblock %} </div> </body>

Child Template
{% extends "base.html" %} {% block title %}Index{% endblock %} {% block content %} <h1>Index</h1> <p class="important"> Welcome on my awesome homepage. </p> {% endblock %}

render_template('child.html')
<html> <head> <link rel="stylesheet" href="style.css" /> <title>Index - My Webpage</title> </head> <body> <div id="content"><h1>Index</h1> <p class="important"> Welcome on my awesome homepage.</p> </div> <div id="footer">&copy; Copyright 2008 by <a href="http://domain.invalid/">you</a>.</div> </body>

more...

Request and Sessions


from flask import Flask, session, redirect, url_for, escape, request app = Flask(__name__) app.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT' @app.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'POST': session['username'] = request.form['username'] return redirect(url_for('index')) return ''' <form action="" method="post"> <p><input type=text name=username> <p><input type=submit value=Login> </form>'''

Request and Sessions


@app.route('/') def index(): if 'username' in session: return 'Logged in as %s' % escape(session ['username']) return 'You are not logged in' @app.route('/logout') def logout(): # remove the username from the session if it's there session.pop('username', None) return redirect(url_for('index'))

Redirects and Errors


from flask import abort, redirect, url_for @app.route('/') def index(): return redirect(url_for('login')) @app.route('/login') def login(): abort(401) @app.errorhandler(404) def page_not_found(error): return render_template('page_not_found.html'), 404

Snippets
import sqlite3 from flask import g def connect_db(): return sqlite3.connect('/path/to/database.db') @app.before_request def before_request(): g.db = connect_db() @app.teardown_request def teardown_request(exception): g.db.close()

many extensions

Flask-Babel Flask-CouchDB Flask-MongoKit Flask-Login Flask-Mail Flask-Script Flask-SQLAlchemy Flask-Testing Flask-WTF ...

Flask-SQLAlchemy
from flask import Flask from flaskext.sqlalchemy import SQLAlchemy app = Flask(__name__) db = SQLAlchemy(app) class User(db.Model): name = db.Column(db.String(40), primary_key=True) email = db.Column(db.String(100)) @@app.route('/user/<name>') def show_user(name): user = User.query.filter_by(name=name). first_or_404() return render_template('user.html', user=user)

Flask-Testing
from flaskext.testing import TestCase from hello import app class AppTest(TestCase): def create_app(self): return app def test_index(self): response = self.client.get('/') self.assert200(response) self.assertTemplateUsed('index.html') title = self.get_cotext_variable('title') self.assertEqual('My Page', title)

larger applications

getting started

Folders and Packages


jusbrasil/ web/ templates/ static/ js/ css/ views/ utils/ main.py config.py tests/ bootstrap.sh manage.py requires.txt jusbrasil/ web/ templates/ noticias/ index.html view.html jurisprudencia/ views/ __init__.py noticias.py jurisprudencia.py diarios.py legislacao.py ...

Create App
def create_app(config=None, app_name=None, blueprints=None): app_name = app_name or __name__ app = flask.Flask(app_name, static_folder=None) configure_app(app, config) configure_blueprints(app, blueprints) configure_error_handlers(app) configure_database(app) configure_context_processors(app) configure_template_filters(app) configure_before_request(app) configure_extensions(app) configure_views(app) configure_admin(app) return app

Catching HTTP Errors

def configure_error_handlers(app): @app.errorhandler(401) def unauthorized(error): flask.flash(u'Para acessar voc precisa estar logado.') return flask.redirect(url_for('login', next=request.url, _external=True)) @app.errorhandler(404) def page_not_found(error): return render("errors/page_not_found.html"), 404 @app.errorhandler(500) def server_error_page(error): return render("errors/server_error.html"), 500

Own Extensions
from jusbrasil.search import pool from thriftclient import ThriftClient class SearchClient(ThriftClient): def __init__(self, app=None): ThriftClient.__init__(self) self.app = None if app is not None: self.init_app(app) def init_app(self, app): if not app.config['TESTING']: self.pool = pool.ConnectionPool( server_list=app.config['SEARCH_SERVERS'], timeout=app.config['SEARCH_TIMEOUT'], pool_size=app.config['SEARCH_POOL_SIZE']) self.app = app

Logged Users
def get_current_user(): return getattr(flask.g, 'user', None) def configure_before_request(app): @app.before_request def load_current_user(): flask.g.user = None if 'user' in session: flask.g.user = load_user_session(session['user'])

Shortcuts
from jusbrasil.web import cache @cache.memoize() def get_object_or_404(id, document_type): # no valid id if id < 0: abort(404) _object = client.getDocument(document_type, id) # document not found if _object is None: abort(404) return _object

modularize

Blueprints
import flask from jusbrasil.web.utils.shortcuts import get_object_or_404 app = flask.Blueprint('jurisprudencia', __name__) @app.route('/<int:id>/<slug>') def view(id, slug): juris = get_object_or_404(id, 'jurisprudencia') template = 'jurisprudencia/view.html' return render(template, juris=juris)

Templates
{% block title %} {{ juris.title }} {% endblock %} {% block content %} <div class="jurisprudencia"> <h1>{{ juris.titulo }}</h1> <div class="ementa">{{ juris.ementa }}</div> <a href="{{ juris.inteiroTeor }}">Download Inteiro Teor</a> <div class="acordao"> <h2>Acrdo</h2> {{ juris.acordao }}</div> </div> {% endblock %}

finally...

Flask is a microframework for Python based on Werkzeug, Jinja 2 and good intentions.

scaling

Osvaldo Matos-Junior tupy@jusbrasil.com.br

Q& A

References
http://denied.immersedcode.org/ http://flask.pocoo.org/ http://jinja.pocoo.org/ http://werkzeug.pocoo.org/ http://www.quora.com/Flask https://github.com/mitsuhiko/flask http://www.slideshare.net/mitsuhiko/flask https://github.com/italomaia/flask-empty

You might also like