KEMBAR78
Flask intro - ROSEdu web workshops | PDF
Flask intro
             rosedu web workshops



27/03/2013                            alex@grep.ro
                                    alex@eftimie.ro
prerequisites              what we'll do
●   python      ●   set-up a working environment
●   bash        ●   run a local server
●   HTTP        ●   write a twitter clone in a single
                    python file
                ●   use templates
                ●   use an ORM
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-wide
apt-get install python-virtualenv
# create new
virtualenv env
# activate
source env/bin/activate
# install flask
pip install flask

# pip freeze
pip freeze > requirements.txt
basic flask app
#!/usr/bin/env python         (env)student@intel:~$ python mini.
                              py
import flask                   * Running on http://127.0.0.1:
                              5000/
app = flask.Flask(__name__)   127.0.0.1 - - [27/Mar/2013 01:13:
app.config['DEBUG'] = True    28] "GET / HTTP/1.1" 200 -


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


if __name__ == '__main__' :
    app.run()
new message form
●     create a file new.html inside a templates/ folder

    <h1>New message </h1>

    <form method="post">
        <textarea rows="4" cols="80" name="message"></textarea>
        <br>
        <button type="submit">send</button>
    </form>


●     route /new to a view function rendering the template

...
      return flask.render_template( 'new.html' )


                                                                  (see home())
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
                                                        messages.html
●   change print "Hello World" with
    template rendering and context      <h1>Mini Twitter </h1>

                                        <p>
flask.render_template( 'messages.         <a href="{{ url_for('new') }}" >new msg</a>
html',                                  </p>
       messages =Message.query.all())
                                        {% for message in messages %}
                                        <article>
●   create html file in templates/        <p>{{ message.text }} </p>
                                          <footer>
                                             <time>{{message.time}} </time>
●   use {{ variable }} to display         </footer>
                                        </article>
    variable value in template          {% endfor %}

●   call url_for for view permalink
●   use {% for %} to iterate through
    messages
template filters
Usage:                     You:
{{ variable |filter }}      ● display message time in local
                              time
Custom:
@app.template_filter()
def tolower(value):
    return value.lower()
common layout template
layout.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 username
    if 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 templates
    @app.before_request
    def 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
●   use
    @app.route('/private')
    @login_required
    def private_view():
        ...
●   you: decorate new()
person model
●   db model with id (Integer, primary_key) and username
    (String)
●   message author
    class 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')
}}">
                                          /* style.css */
●      wrap {% block content %} in <div class="container"> </div>
                                   * { 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 up
we learned about:                 file structure:
●   web application
                                  mini.py
●   url routing                   settings.py
                                  static/
●   templates
                                      style.css
●   sql                           templates/
                                      header.html
●   sessions
                                      layout.html
                                      messages.html
                                      new.html


next:
●   deployment options
●   security, social extensions
see more
● full source code
  https://github.com/mgax/minitwitter


● flask documentation
  http://flask.pocoo.org/docs/

Flask intro - ROSEdu web workshops

  • 1.
    Flask intro rosedu web workshops 27/03/2013 alex@grep.ro alex@eftimie.ro
  • 2.
    prerequisites what we'll do ● python ● set-up a working environment ● bash ● run a local server ● HTTP ● write a twitter clone in a single python file ● use templates ● use an ORM
  • 3.
    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)
  • 4.
    virtualenv, pip # installsystem-wide apt-get install python-virtualenv # create new virtualenv env # activate source env/bin/activate # install flask pip install flask # pip freeze pip freeze > requirements.txt
  • 5.
    basic flask app #!/usr/bin/envpython (env)student@intel:~$ python mini. py import flask * Running on http://127.0.0.1: 5000/ app = flask.Flask(__name__) 127.0.0.1 - - [27/Mar/2013 01:13: app.config['DEBUG'] = True 28] "GET / HTTP/1.1" 200 - @app.route( '/') def home(): return "Hello World!" if __name__ == '__main__' : app.run()
  • 6.
    new message form ● create a file new.html inside a templates/ folder <h1>New message </h1> <form method="post"> <textarea rows="4" cols="80" name="message"></textarea> <br> <button type="submit">send</button> </form> ● route /new to a view function rendering the template ... return flask.render_template( 'new.html' ) (see home())
  • 7.
    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')
  • 8.
    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)
  • 9.
    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'
  • 10.
    fetch from db messages.html ● change print "Hello World" with template rendering and context <h1>Mini Twitter </h1> <p> flask.render_template( 'messages. <a href="{{ url_for('new') }}" >new msg</a> html', </p> messages =Message.query.all()) {% for message in messages %} <article> ● create html file in templates/ <p>{{ message.text }} </p> <footer> <time>{{message.time}} </time> ● use {{ variable }} to display </footer> </article> variable value in template {% endfor %} ● call url_for for view permalink ● use {% for %} to iterate through messages
  • 11.
    template filters Usage: You: {{ variable |filter }} ● display message time in local time Custom: @app.template_filter() def tolower(value): return value.lower()
  • 12.
    common layout template layout.html <!doctype html> <html> ... {% block content %} {% endblock %} </html> messages.html {% extends 'layout.html' %} {% block content %} goodies... {% endblock %}
  • 13.
    config file ● move configuration to a file app.config.from_pyfile( 'settings.py' ) ● settings DEBUG = True ...
  • 14.
    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' %}
  • 15.
    login ● view + template the same as new() - new.html ● handle the submitted username if 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 %}
  • 16.
    session ● store something in session flask.session['username' ] = username ● fetch and expose in templates @app.before_request def 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 %}
  • 17.
    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
  • 18.
    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 ● use @app.route('/private') @login_required def private_view(): ... ● you: decorate new()
  • 19.
    person model ● db model with id (Integer, primary_key) and username (String) ● message author class 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) ...
  • 20.
    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>
  • 21.
    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> ...
  • 22.
    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') }}"> /* style.css */ ● wrap {% block content %} in <div class="container"> </div> * { 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 }
  • 23.
    wrapping up we learnedabout: file structure: ● web application mini.py ● url routing settings.py static/ ● templates style.css ● sql templates/ header.html ● sessions layout.html messages.html new.html next: ● deployment options ● security, social extensions
  • 24.
    see more ● fullsource code https://github.com/mgax/minitwitter ● flask documentation http://flask.pocoo.org/docs/