This document provides an overview of setting up a Flask application with common extensions for functionality like user authentication, database management, forms validation, and sending email. It demonstrates initializing extensions like Flask-SQLAlchemy, Flask-Login, Flask-Migrate, Flask-Script, and Flask-Mail. Models and views are defined to handle user registration and login. The Flask application is configured and commands are provided to initialize the database, run migrations, and start a development server.
11. MANAGE.PY
#! /usr/bin/env python
import os
from flask.ext.script import Manager
from flaskfilled import create_app
app = create_app(os.getenv('FLASK_CONFIG') or 'default')
manager = Manager(app)
if __name__ == '__main__':
manager.run()
12. SHELL WITH CONTEXT
from flask.ext.script import Shell
def make_shell_context():
return dict(app=app)
manager.add_command('shell', Shell(make_context=make_shell_context))
13. $ python manage.py
usage: manage.py [-?] {runserver,shell} ...
positional arguments:
{runserver,shell}
runserver Runs the Flask development server i.e. app.run()
shell Runs a Python shell inside Flask application context.
optional arguments:
-?, --help show this help message and exit
16. FLASK-SQLALCHEMY
Single wrapper for most of SQLAlchemy
Preconfigured scope session
Sessions are tied to the page lifecycle
pip install flask-sqlalchemy
17. __INIT__.PY
from flask.ext.sqlalchemy import SQLAlchemy
db = SQLAlchemy()
def create_app(config_name):
app = Flask(__name__)
app.config.from_object(config[config_name])
db.init_app(app)
return app
19. FLASKFILLED/MODELS.PY
from flaskfilled import db
class Cookie(db.Model):
__tablename__ = 'cookies'
cookie_id = db.Column(db.Integer(), primary_key=True)
cookie_name = db.Column(db.String(50), index=True)
cookie_recipe_url = db.Column(db.String(255))
quantity = db.Column(db.Integer())
20. MANAGE.PY
from flask.ext.script import Command
from flaskfilled import db
from flaskfilled.models import Cookies
def make_shell_context():
return dict(app=app, db=db)
class DevDbInit(Command):
'''Creates database tables from sqlalchemy models'''
def __init__(self, db):
self.db = db
def run(self):
self.db.create_all()
21. $ python manage.py db_init
$ python manage.py shell
In [1]: db.metadata.tables
Out[1]: immutabledict({'cookies': Table('cookies', 'stuff')})
In [2]: from flaskfilled.models import Cookie
22. c = Cookie(cookie_name="Chocolate Chip",
cookie_recipe_url="http://zenofthecookie.com/chocolatechip.html"
quantity=2)
db.session.add(c)
db.session.commit()
27. GENERATING A MIGRATION
$ python manage.py db migrate -m "initial migration"
INFO [alembic.migration] Context impl SQLiteImpl.
INFO [alembic.migration] Will assume non-transactional DDL.
Generating flask-filled/migrations/versions/586131216f6_initial_migration.p
28. RUNNING MIGRATIONS
$ python manage.py db upgrade
INFO [alembic.migration] Context impl SQLiteImpl.
INFO [alembic.migration] Will assume non-transactional DDL.
INFO [alembic.migration] Running upgrade -> 586131216f6, initial migration
31. FLASKFILLED/__INIT__.PY
from flask.ext.login import LoginManager
login_manager = LoginManager()
def create_app(config_name):
app = Flask(__name__)
app.config.from_object(config[config_name])
db.init_app(app)
login_manager.setup_app(app)
from .main import main as main_blueprint
app.register_blueprint(main_blueprint)
from .auth import auth as auth_blueprint
app.register_blueprint(auth_blueprint, url_prefix='/auth')
32. MODELS.PY
from werkzeug.security import generate_password_hash, check_password_hash
from flaskfilled import login_manager
class User(db.Model, UserMixin):
__tablename__ = 'users'
id = db.Column(db.Integer(), primary_key=True)
username = db.Column(db.String, primary_key=True)
password = db.Column(db.String)
authenticated = db.Column(db.Boolean, default=False)
33. USER MODEL REQUIRED METHODS
PROVIDED BY USERMIXIN
def is_active(self):
return True
def get_id(self):
return self.id
def is_authenticated(self):
return self.authenticated
def is_anonymous(self):
return False
34. USER MODEL PASSWORD HANDLING
@property
def password(self):
raise AttributeError('password is not a readable attribute')
@password.setter
def password(self, password):
self.password_hash = generate_password_hash(password)
def verify_password(self, password):
return check_password_hash(self.password_hash, password)
35. SETTING UP THE AUTH BLUEPRINT
AUTH/__INIT__.PY
from flask import Blueprint
auth = Blueprint('auth', __name__)
from . import views
36. AUTH/VIEWS.PY
from flask import render_template, redirect, request, url_for, flash
from flask.ext.login import login_user, logout_user, login_required
from . import auth
from flaskfilled.models import User
37. LOGIN
@auth.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
username = request.form.get('username', '')
password = request.form.get('password', '')
user = User.query.filter_by(username=username).first()
if user is not None and user.verify_password(password):
login_user(user)
next = request.args.get('next')
return redirect(next or url_for('main.index'))
else:
flash('Wrong username or password.')
return render_template('auth/login.html')
40. MAIN/VIEWS.PY
from flask import render_template
from . import main
@main.route('/', methods=['GET'])
def index():
return render_template('main/index.html')
41. INDEX TEMPLATE
{% extends "base.html" %}
{% block title %}The Index{% endblock %}
{% block page_content %}
{% if not current_user.is_authenticated() %}
<p><a href="{{ url_for('auth.login') }}">Click here to login</a>.</p
{% else %}
<p><a href="{{ url_for('auth.logout') }}">Click here to logout</a>.</
{% endif %}
{% endblock %}
42. CREATE USERS MIGRATION AND
APPLY IT
$ python manage.py db migrate -m "User"
Generating /Users/jasonamyers/dev/flask-filled/migrations/versions/8d9327f0
$ python manage.py db upgrade
INFO [alembic.migration] Running upgrade 586131216f6 -> 8d9327f04f, User
51. AUTH/FORMS.PY
from flask.ext.wtf import Form
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import Required, Length
class LoginForm(Form):
username = StringField('username', validators=[Required(),
Length(1, 64)])
password = PasswordField('Password', validators=[Required()])
submit = SubmitField('Log In')
52. AUTH/VIEWS.PY
@auth.route('/login', methods=['GET', 'POST'])
def login():
form = LoginForm()
if form.validate_on_submit():
user = User.query.filter_by(username=form.username.data).first()
if user is not None and user.verify_password(form.password.data):
login_user(user)
next = request.args.get('next')
return redirect(next or url_for('main.index'))
else:
flash('Wrong username or password.')
return render_template('auth/login.html', form=form)
53. TEMPLATES/AUTH/LOGIN.HTML
{% block page_content %}
<div class="col-md-4">
<form action="" method="POST">
{{ form.csrf_token }}
{% if form.csrf_token.errors %}
<div class="warning">You have submitted an invalid CSRF token</
{% endif %}
{{form.username.label }}: {{ form.username }}
{% if form.username.errors %}
{% for error in form.username.errors %}
{{ error }}
{% endfor %}
{% endif %}<br>
{{form.password.label }}: {{ form.password }}
{% if form.password.errors %}
{% for error in form.password.errors %}
{{ error }}
57. MODELS.PY
roles_users = db.Table('roles_users',
db.Column('user_id', db.Integer(),
db.ForeignKey('users.user_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)
name = db.Column(db.String(80), unique=True)
description = db.Column(db.String(255))
58. MODELS.PY - USER CLASS
class User(db.Model, UserMixin):
roles = db.relationship('Role', secondary=roles_users,
primaryjoin=user_id == roles_users.c.user_id
backref='users')
59. MODELS.PY - IDENTITY LOADER
@identity_loaded.connect
def on_identity_loaded(sender, identity):
# Set the identity user object
identity.user = current_user
# Add the UserNeed to the identity
if hasattr(current_user, 'id'):
identity.provides.add(UserNeed(current_user.id))
# Assuming the User model has a list of roles, update the
# identity with the roles that the user provides
if hasattr(current_user, 'roles'):
for role in current_user.roles:
identity.provides.add(RoleNeed(role.name))
60. AUTH/VIEWS.PY
from flask import current_app
from flask.ext.principal import identity_changed, Identity
@auth.route('/login', methods=['GET', 'POST'])
def login():
form = LoginForm()
if form.validate_on_submit():
user = User.query.filter_by(username=form.username.data).first()
if user is not None and user.verify_password(form.password.data):
login_user(user)
identity_changed.send(current_app._get_current_object(),
identity=Identity(user.user_id))
next = request.args.get('next')
return redirect(next or url_for('main.index'))
else: