ABOUT ME... $ whoami
nobody
srsly?!! I'm a freelance developer who likes to keep on the dl
I've worked on some interesting things
SO WHAT IS FLASK?From flask's : website
Flask is a microframework for Pythonbased on Werkzeug, Jinja 2 and good
intentions.
$ pip install flask
WHAT DOES IT LOOK LIKE? from flask import Flaskapp = Flask(__name__)
@app.route("/")def hello(): return "Hello World!"
if __name__ == '__main__': app.run()
$ python hello.py
WELL, THAT LOOKS EASY!It really is!!!
The best thing about flask is YOU get to pick and choosewhat's BEST for the problem being solved.
The downside is you can also shoot yourself in the foot if youdon't think about security! Never trust your users!
BEFORE WE (REALLY) BEGIN...This is not so much of an introduction but more about the
lay of the land, alternatives, and some best practices.
There is no replacement for the fine documentation thatflask nor the packages mentioned in this presentation
provide.
PROJECT LAYOUTSDepending on your project, you could have just a simple
single script acting as a API service, a couple of filesseparating concerns, or a massive site with multiple
applications within.
Flask is flexible no matter what your project needs.
A SIMPLE SETUP/ project_root|+ templates| | layout.html| | index.html| | navigation.html (this would be included in many pages)||+ static (this would be akin to the public directory)| | favicon.ico| | something.js| | styles.css|| main.py
SIMPLISTIC SEPARATION OFCONCERNS
/ project_root|+ templates| | layout.html| | index.html| | navigation.html|+ static| | favicon.ico| | something.js| | styles.css| app.py (application setup w/ configuring of extensions)| forms.py (collection of classes for building forms)| models.py (application data models)| views.py (all view functions/class based views)| main.py (main script importing the app and views)
SIMILAR TO DJANGO/ project_root|+ templates (sitewide templates)|+ static (sitewide static files)|+ blog (flask calls these blueprints)| |+ templates (template files specific to the blueprint)| |+ static (static files specific to the blog blueprint)| | models.py| | forms.py| | commands.py| | views.py| app.py (factory function for creating the app)| manage.py (cli/tasks performed on the application itself)| settings.py (application configuration)
Django 'apps' => Flask 'blueprints'
CONFIGURATIONAt the heart of every project is your configuration settings.
Flask can be configured in so many ways (by python objects,external files, etc). Generally, extension setup would be in
here as well.
SINGLE FILE SETUPfrom flask import Flask...SQLALCHEMY_DATABASE_URI = "mysql://appuser:[email protected]/db"SECRET_KEY = "SuperSecretKeyShouldNOTBeHEREEVER"...app = Flask(__name__)app.config.from_object(__name__) # uses constants in file
When people first start off, they would end up doing this.Please don't do this.
EXTERNAL CONFIG FILEimport os...class Config(object): # py2 friendly ;P SECRET_KEY = os.environ.get('APP_ENV', 'SuperSecretKeyDefault') TESTING = False ...class DevelopmentConfig(Config): ... # key overrides specific to environment
class ProductionConfig(Config): ... # key overrides specific to environment
config = 'dev': DevelopmentConfig, 'prod': ProductionConfig, 'default'
This is an improvement over embedding your config keys inyour main app file for someone to see the gory details.
USE DOTENV$ pip install pythondotenv
Helpful for working with people on a project who don'tknow how to setup environment variablesComes with a CLI for editing keys but very easy to do byhandConfiguration file can be checked into version control but.env does notConfig file (settings.py -- by convention) changes slightly
DOTENV EXAMPLE.env (in same dir as settings.py)
DEV_SECRET_KEY="somerandomgibberish or os.urandom output ;)"BCRYPT_LEVEL=5000 # just kidding more like 13ish RTFM...
import osfrom os.path import dirname, joinfrom dotenv import load_dotenv
dotenv_path = join(dirname(__file__), '.env')load_dotenv(dotenv_path)
class Config(object): BCRYPT_LEVEL = os.environ.get('BCRYPT_LEVEL') # provide no default! ...
class DevelopemntConfig(Config): SECRET_KEY = os.environ.get('DEV_SECRET_KEY') # namespaced secret key
JINJA2 FOR TEMPLATES(MAIN HIGHLIGHTS)
Comes out of the boxSupports template inheritance, includes, and blocksEscapes output to prevent XSS by defaultHas a lot of filters, support for macros and isn't just forhtml
WHAT DOES IT LOOK LIKE% extends "layout.html" %% block body % <ul> % for user in users % <li><a href=" user.url "> user.username </a></li> % endfor % </ul>% endblock %
from flask import Flask, render_templateapp = Flask(__name__)
@app.route("/")def index(): ... return render_template("templates/example.html", user=user)
if __name__ == '__main__': app.run()
ALTERNATIVE TEMPLATINGMako Templates (which could also use Plim)PyJade (personal favorite)
supports django and mako templateseventual project name change due to 'jade' TM/SMissue
FORMSA majority of the time a form is used for interacting with a
website and its pretty easy to do with a jinja macro but is notideal
JINJA MACRO EXAMPLE RENDERINGA FORM INPUT
% macro input(name, value='', type='text', size=20) % <input type=" type " name=" name " value=" value|e " size% endmacro %
Jinja is used for the presentation layer of your applicationand does not deal with data coming in so I would not
recommend using this method.
NEVER TRUST DATA COMING IN OR OUT
WTFORMS / FLASK-WTFfrom flask_wtf import Formfrom wtforms.fields import StringFieldfrom wtforms.validators import DataRequired
class UserForm(Form): name = StringField(u"Name", validators=[DataRequired()])
<form method="post"> form.csrf_token form.name.label form.name(size=20) <input type="submit" value="Submit"></form>
Flask-WTF integrates WTForms with CSRF protection,Recaptcha support, uploads, html5 widgets, and
internationalization.
DATA STORAGEFlask is flexible in that you aren't tied to an RDBMS and can
choose which data store best fits your needs (althoughPostgres can solve a majority of them).
Python has many hooks into whatever data store you'reusing or haven't even considered.
ORMS + ODMSORMs (Object Relation Mapper) and ODMs (Object
Document Mapper) are pythonic objects mapping to theschema/structure of your data store. ORMs are for relational
databases and ODMs are for NoSQL databases.
BUT I DON'T NEED NO ORM/ODMSure you could write raw sql or use pymongo and no one
would stop you but you would be missing out on someimportant things...
business logic/validation pertaining to a model/unit (non-anemic models)testability
The options listed offer you a way to drop down to raw sql ifyou need to solve that problem.
SQLALCHEMY (ORM)from sqlalchemy import *from sqlalchemy.ext.declarative import declarative_basefrom sqlalchemy.orm import relation, sessionmaker
Base = declarative_base()
class User(Base): __tablename__ = "users" id = Column(Integer, primary_key=True) first_name = Column(String(20), nullable=False) last_name = Column(String(100), nullable=False) ...
SQLALCHEMY PROS + CONTRASPros Contras
Well Documented Documentation can be hard togrep
Very Mature & battletested
Pretty big code base if you areworking on an embeded device ormobile application
Supports all majordatabase vendors &custom types
Migrations viaAlembic
Alembic
FLASK-SQLALCHEMYThis package handles the setup, (sqlalchemy) sessions, and
teardown within the flask context$ pip install FlaskSQLAlchemy
Migrations are supported via Flask-Migrate$ pip install FlaskMigrate
PEEWEE (ORM)from peewee import *
db = SqliteDatabase('products.db')
class Product(Model): name = CharField(max_length=40, unique=True) price = DecimalField(constraints=[Check("price < 10000")]) created = DateTimeField()
class Meta: database = db
$ pip install peewee
PEEWEE PROS + CONTRASPros Contras
Very small and expressive(1 module)
Not as popular asSQLAlchemy
Excellent choice forrestricted resourceenvironments (IoT /Mobile apps)
Only supports MySQL, Sqlite,Postgres. SQLCipher andBerkleyDB via extensions
Similar to Django ORM
FLASK-PEEWEE?There used to be an extension which provided an api and
admin interface but is now used via peewee's playhouse (setof extensions that come with the package)
from playhouse.flask_utils import FlaskDB...database = FlaskDB(app)
class User(database.Model): username = CharField(max_length=40, unique=True)
Migrations are supported but not as nice as alembic. Usearnold for migration generation. Migrations always have to
be configured.$ pip install arnold
FLASK-MONGOALCHEMY (ODM)...from flask.ext.mongoalchemy import MongoAlchemy...db = MongoAlchemy(app)
class Author(db.Document): name = db.StringField()
class Book(db.Document): title = db.StringField() author = db.DocumentField(Author) year = db.IntField()
$ pip install FlaskMongoAlchemy
NoSQL databases don't need/use migrations since theschema/document is dynamic in nature
HONORABLE MENTIONALCHY (ORM)
Use Alchy if you are trying to use your models outside of aflask app context as this provides some extra enhancements
to the model classes, sessions and queries.
A drop in replacement for Flask-SQLAlchemy.$ pip install alchy
HONORABLE MENTIONMONGOENGINE (ODM)
Flask-MongoEngine is the extension for integratingMongoEngine into your flask project. MongoEngine also
works with Django.$ pip install flaskmongoengine
USER MANAGEMENTWeb applications have users and you have to be careful with
authentication. Since flask can use various packagesinterchagably you have to know what you are trying to
achieve.
FLASK-LOGINThe de facto flask extension in user session management.
store active user ID in session with log in and outrestrict views whether the user is logged in or nothandles 'remember me' functionalityhelps protect user sessions by 'cookie thieves'possible integration with Flask-Principal or otherauthorization packages$ pip install FlaskLogin
ROLL YOUR OWN USER CLASSfrom flask_login import UserMixinfrom ..extensions import db, flask_bcrypt
class User(UserMixin, db.Model): __tablename__ = 'users' id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(80), unique=True, nullable=False) ... # These methods are overridden by your custom User class def is_authenticated(self): ... def is_active(self): ... def is_anonymous(self): ... def get_id(self): ...
A WARNING TO THOSE WHO AREN'TAWARE OF THE DARK ARTS
I would not recommend storing user information that issensitive whatsoever! Store only what is pertinent to your
application and use an external service for handlingauthentication like OpenID, LDAP, etc.
If you really have to store passwords you can use passlib orbcrypt, create the hash, store it and check the hashes when
authenticating.$ pip install passlib
$ pip install flaskbcrypt # or pip install flaskscrypt
OR USE SOMEONE ELSE'S$ pip install flaskuser
This package offers a lot of features that would be good forsomeone starting out and uses a lot of packages that I
have/will talk about.
Sometimes an app doesn't require all of these bells andwhistles and you end up being dependent upon an
implementation.
ROLE MANAGEMENTUsers can have many types of roles within an application.
You can have various roles associated with a user.
There are a couple of packages that can be tied into yourUser class implementation along with Flask-Login to fine
tune resource management.
FLASK-PRINCIPALThis package provides very fine tuned role management and
splits things up into Permissions and needs for theRole/User.
Read the documentation thoroughly when setting this up.
You will also have to create a relation/join table betweenvarious Role classes to the User class.
EXAMPLE MODELS.PY...roles_users = db.Table( 'roles_users', db.Column('id', db.Integer(), primary_key=True), db.Column('user_id', db.Integer(), db.ForeignKey('users.id')), db.Column('role_id', db.Integer(), db.ForeignKey('roles.id')))
class Role(db.Model): __tablename__ = 'roles' id = db.Column(db.Integer, primary_key=True) ...
class User(db.Model): __tablename__ = 'users' ...
EXAMPLE FLASK-PRINCIPAL SETUP...from flask_login import LoginManager, current_userfrom flask_principal import ( Principal, Permission, RoleNeed, UserNeed, identity_loaded)from .models import User...# flasklogin initialization should happen before flaskprincipal# as an instance of LoginManager is used for managing user sessions...principals = Principal()...admin_permission = Permission(RoleNeed('admin'))
@identity_loaded.connectdef on_identity_loaded(sender, identity): identity.user = current_user
HONORABLE MENTIONFLASK-AUTH
from flaskext.auth.permissions import Permission, Role
user_create = Permission('user', 'create')user_view = Permission('user', 'view')
roles = 'admin': Role('admin', [user_create, user_view]), 'userview': Role('userview', [user_view]),
def load_role(role_name): return roles.get(role_name)
auth.load_role = load_role
$ pip install flaskauth
HONORABLE MENTIONFLASK-BOUNCER (CANCAN INSPIRED)
from flask.ext.bouncer import requires, ensure, Bouncerapp = Flask()bouncer = Bouncer(app)
@bouncer.authorization_methoddef define_authorization(user, they): if user.is_admin: they.can(MANAGE, ALL) else: they.can(READ, ('Article', 'BlogPost')) they.can(EDIT, 'Article', lambda a: a.author_id == user.id)
@app.route("/articles")@requires(READ, Article)def articles_index(): return "A bunch of articles"
$ pip install flaskbouncer
ADMIN INTERFACEWith flask you can definitely roll your own admin section forall of your CRUD operations, but why do that when someone
else has done the heavy liing?
FLASK-ADMINThis is a popular admin interface and has excellent support
for SQLAlchemy, MongoEngine, Peewee and PyMongo.
Easily customizable and has a lot of features like CSRFprotection, localization, can manage files and folders and
can add model backends if its not supported.$ pip install FlaskAdmin
HONORABLE MENTIONFLASK-SUPERADMIN
Has a very nice interface and supports only MongoEngine,Django and SQLAlchemy models. Also has an optional File
admin. This project was originally forked from Flask-Admin.$ pip install FlaskSuperAdmin
TASK MANAGEMENTTasks are for things that need to happen with your flask appbut outside of the request-response life cycle. Simple tasks
could be setting up your database, seeding it or evenrunning your tests. There are 2 options for running CLI
scripts with your flask application right now.
FLASK-SCRIPT (0.10 UP TO 1.0)This is the current option that some of the pre-existing flask-
extensions use as an interface with the CLI and is trivial touse. It takes an app instance and you can even have a shell
for inspecting your flask app.
FLASK-SCRIPT EXAMPLEmanage.py -- by convention
...from flask_script import Managerfrom flask_script.commands import Shellfrom flask_migrate import MigrateCommandfrom sample_app.app import create_appfrom sample_app.extensions import db
app = create_app(os.environ.get('SAMPLE_APP_ENV', 'default'))
manager = Manager(app)manager.add_command('db', MigrateCommand)
@manager.commanddef test(): "Run unittests" import unittest
CLICK (0.11 ONWARDS)Flask 0.11 comes with a dependency of the Click package. It
has a similar style of using decorators declaring clicommands which is different if you are used to using the
standard argparse.
Flask now provides a 'flask' command to run your scripts.
Want to run a shell with your flask app? Run `flask shell` atthe command line
CLICK EXAMPLE# win32/64 users should use 'set' instead of exportexport FLASK_APP=/path/to/main/script.py
import clickfrom flask import Flask
app = Flask(__name__)
@app.cli.commanddef hello(): """Saying hello""" click.echo("Saying hello...")
$ flask helloSaying hello...
This is as simple as it gets but should be discussed aer
ODDS + ENDSThere is just way too much to cover and I have only covered
the basics without even going into anything. Flask haspackages for almost everything from webassets,
websockets, A/B testing and even celery ;P
My goal was to give you a roadmap to help you choosewhich packages could benefit you in the future.