flask intro - rosedu web workshops
DESCRIPTION
The slides we used for a hands on workshop on Flask - micro web framework at http://events.rosedu.org/web-workshopsTRANSCRIPT
Flask introrosedu web workshops
27/03/2013 [email protected] [email protected]
prerequisites ● python
● bash
● HTTP
● set-up a working environment
● run a local server
● write a twitter clone in a single
python file
● use templates
● use an ORM
what we'll do
was ist flask?● micro web framework
● WSGI (same as django, webapp2)
● decoupled
● werkzeug routing
● jinja2 templates
● wtf forms
● many extensions
● write web applications not web scripts (such as PHP does)
virtualenv, pip# install system-wideapt-get install python-virtualenv# create newvirtualenv env# activatesource env/bin/activate# install flaskpip install flask
# pip freezepip freeze > requirements.txt
basic flask app#!/usr/bin/env python
import flask
app = flask.Flask(__name__)app.config['DEBUG'] = True
@app.route('/')def home(): return "Hello World!"
if __name__ == '__main__': app.run()
(env)student@intel:~$ python mini.py * Running on http://127.0.0.1:5000/
127.0.0.1 - - [27/Mar/2013 01:13:28] "GET / HTTP/1.1" 200 -
new message form● create a file new.html inside a templates/ folder
● route /new to a view function rendering the template
...return flask.render_template('new.html')
(see home())
<h1>New message</h1>
<form method="post"> <textarea rows="4" cols="80" name="message"></textarea>
<br> <button type="submit">send</button></form>
form submit; redirect ● check request method (one of 'GET' or 'POST')
flask.request.method == 'POST'
● get post data, print it
print flask.request.form['message']
● redirect to home page
flask.redirect(flask.url_for('home'))
@app.route('/new', methods=['GET', 'POST'])def new():
if flask.request.method == 'POST':print "msg:", flask.request.form['message']return flask.redirect(flask.url_for('home'))
return flask.render_template('new.html')
db; message model● in a terminal:pip install SQLAlchemy Flask-SQLAlchemy
● in mini.py
from flask.ext.sqlalchemy import SQLAlchemy...app.config['SQLALCHEMY_DATABASE_URI' ] = 'sqlite:////tmp/test.db'db = SQLAlchemy(app)...
class Message(db.Model): id = db.Column(db.Integer, primary_key =True) text = db.Column(db.String) time = db.Column(db.DateTime)
save to db● create db before app.run()
db.create_all()
● replace print with insert statement, in new()text = flask.request.form[ 'message']message = Message(text=text, time=datetime.utcnow())db.session.add(message)db.session.commit()
● sqlite3 /tmp/test.db 'select * from message'
fetch from db● change print "Hello World" with
template rendering and context
flask.render_template( 'messages.html',
messages=Message.query.all())
● create html file in templates/
● use {{ variable }} to display
variable value in template
● call url_for for view permalink
● use {% for %} to iterate through
messages
messages.html
<h1>Mini Twitter</h1>
<p> <a href="{{ url_for('new') }}" >new msg</a></p>
{% for message in messages %}<article> <p>{{ message.text }} </p> <footer>
<time>{{message.time}} </time> </footer></article>{% endfor %}
template filtersUsage:{{ variable|filter }}
Custom:@app.template_filter()def tolower(value): return value.lower()
You:● display message time in local
time
common layout templatelayout.html
<!doctype html>
<html>
...
{% block content %}
{% endblock %}
</html>
messages.html{% extends 'layout.html' %}
{% block content %}
goodies...
{% endblock %}
config file● move configuration to a file
app.config.from_pyfile('settings.py')
● settings
DEBUG = True
...
flash messages● use session to display messages in the next view
flask.flash("I have a message for you" )
● display messages in template {% for message in get_flashed_messages() %}
<p class="msg"> ... {% endfor %}
● put it in header.html then include it before content block in the layout
template
{% include 'other_template.html' %}
login● view + template the same as new() - new.html
● handle the submitted usernameif flask.request.method == 'POST':
username = flask.request.form['username']...
● print username or flash it
{% extends 'layout.html' %}
{% block content %} <form method="post"> <input name="username"> <button type="submit">login</button> </form>{% endblock %}
session● store something in session
flask.session['username'] = username
● fetch and expose in [email protected]_requestdef get_user(): flask.g.username = flask.session.get('username')
● use in header{% if g.username %} logged in as {{ g.username }}{% else %} <a href="{{ url_for('login') }}" >login</a>{% endif %}
logout● clean the session in style
flask.session.pop('variable', '')
● you○ write a view logout()
○ route /logout to it
○ delete username from session
○ flash the message "bye bye"
○ redirect to home page
○ link to it in header
login required decorator● redirect to login if not authenticated
def login_required(func): @wraps(func) def wrapper(*args, **kwargs): if flask.g.username is None: return flask.redirect('login') return func(*args, **kwargs) return wrapper
● [email protected]('/private')
@login_required
def private_view():...
● you: decorate new()
person model● db model with id (Integer, primary_key) and username
(String)
● message authorclass Message(db.Model):
...person_id = db.Column(db.Integer, db.ForeignKey('person.id'))person = db.relationship('Person',
backref=db.backref('messages', lazy='dynamic'))
● get or create Person@classmethod
def get_or_create(cls, username): person = cls.query.filter_by(username =username).first() if person is None: person = cls(username=username)
...
message and person● when adding a message to db
text = flask.request.form['message']person = Person.get_or_create(flask .g.username)message = Message(text=text, time=datetime.utcnow(), person=person)
● when displaying in template<p>
<strong>{{ message.person.username }}: </strong>{{ message.text }}
</p>
person and messages● add view for a person's feed
● route it to /<username>@app.route('/<username>')
● get person or raise http 404 person = Person.query.filter_by(username =username).first_or_404()
● display messages in template
flask.render_template('messages.html', messages=person.messages)
● show link to person's feed...{% set username = message.person.username %}{% set url = url_for('person_feed', username=username) %}<strong><a href="{{ url }}">{{ username }}</a>:</strong>...
static assets● create a folder static/
● add style.css and some ninja CSS
● link to it in layout.html
<link rel="stylesheet" href="{{ url_for('static', filename='style.css')
}}">
● wrap {% block content %} in <div class="container"> </div>/* style.css */
* { font-family: Ubuntu, arial }body { background-color: #efefef }.container { width: 800px; margin: 0 auto; background-color: #fff; }p.msg { background-color: #99ff99; border-radius: 20px; border: 1px solid green; color: green: }a { text-decoration: none }
wrapping upwe learned about:
● web application
● url routing
● templates
● sql
● sessions
next:
● deployment options
● security, social extensions
file structure:
mini.py
settings.py
static/
style.css
templates/
header.html
layout.html
messages.html
new.html
see more
● full source codehttps://github.com/mgax/minitwitter
● flask documentationhttp://flask.pocoo.org/docs/