Flask is a microframework for Python based on Werkzeug, Jinja 2 and good intentions. And before you ask: It's BSD licensed!
from flask import Flask, render_template
app = Flask(__name__)
@app.route("/")
def hello():
return render_template('hello.html', message="Hello World!")
if __name__ == "__main__":
app.run()
<html>
<body>
{{ message }}
</body>
</html>
> cd hello-flask
> virtualenv venv
> source venv/bin/activate
> pip install flask
> python hello.py
* Running on http://localhost:5000/
Instead of creating your app as
from flask import Flask
app = Flask(__name__)
wrap it in a function
from flask import Flask
def factory():
app = Flask(__name__)
return app
Instead of accessing attributes of the app
object
from app import app
app.config['RELEVANT_CONFIG_VARIABLE']
use the Flask current_app
function
from flask import current_app
current_app.config['RELEVANT_CONFIG_VARIABLE']
What's the big deal?
app
What's the big deal?
app
What's the big deal?
app
from app import app
-> circular importsA more thorough example:
from flask import Flask
from app.utils.processors import register_processors
from app.models import db
from app.views.index import index_bp
from app.views.admin import admin
def create_app(config=None, environment=None):
app = Flask(__name__)
app.config['ENVIRONMENT'] = environment
app.config.update(config or {})
db.init_app(app)
admin.init_app(app)
app.register_blueprint(index_bp)
register_processors(app)
return app
Most Flask extensions provide a init_app
method.
For example:
class FlaskExtension(object):
def __init__(self, app=None):
if app:
self.init_app(app)
def init_app(self, app):
if not hasattr(app, 'extensions'):
app.extensions = {}
app.extensions['EXTENSION'] = self
self.app = app
on the extension. current_app
if the app
object is needed. from celery import Celery
from flask import Flask
def make_celery(app):
celery = Celery(app.import_name, broker=app.config['CELERY_BROKER_URL'])
celery.conf.update(app.config)
TaskBase = celery.Task
class ContextTask(TaskBase):
abstract = True
def __call__(self, *args, **kwargs):
with app.app_context():
return TaskBase.__call__(self, *args, **kwargs)
celery.Task = ContextTask
return celery
app = Flask(__name__)
app.config.update(
CELERY_BROKER_URL='redis://localhost:6379',
CELERY_RESULT_BACKEND='redis://localhost:6379'
)
celery = make_celery(app)
@celery.task()
def add_together(a, b):
return a + b
from celery import Celery
celery = Celery('app.factory')
def init_celery(app, celery):
celery.conf.update(app.config)
TaskBase = celery.Task
class ContextTask(TaskBase):
abstract = True
def __call__(self, *args, **kwargs):
with app.app_context():
return TaskBase.__call__(self, *args, **kwargs)
celery.Task = ContextTask
from flask import Flask
form app import celery
from app.utils.celery_util import init_celery
def create_app(config=None, environment=None):
app = Flask(__name__)
app.config.update({'CELERY_BROKER_URL':'redis://localhost:6379'})
init_celery(app, celery)
...
return app
from app import celery
@celery.task()
def add_together(a, b):
return a + b
from app.tasks.add import add_together
add_bp = Blueprint('add', __name__, url_prefix='/add')
@extension_bp.route('/<x>/<y>', methods=('GET',))
def index():
add_together.delay(x,y)
return 'processing'
from app import celery
from app.factory import create_app
from app.utls.celery_util import init_celery
app = create_app()
init_celery(app, celery)
> celery worker -A runcelery.celery --loglevel=debug
In our base template, we likely have something like this near the top of the page.
{% if user.is_authenticated() %}
<div class="signup">
Welcome {{ user.full_name }}!
<a href="{{ url_for('profile', user) }}">(Profile)</a>
</div>
{% else %}
<div class="signup">
Welcome!
<a href="{{ url_for('signup') }}">Signup to join.</a>
</div>
{% endif %}
To handle this
from flask.ext.security import current_user
@bp.route('/gnarly')
def gnarly():
...
return render_template('gnarly.html', user=current_user)
@bp.route('/tubular')
def tubular():
...
return render_template('tubular.html', user=current_user)
@bp.route('/way-cool')
def way_cool():
...
return render_template('way_cool.html', user=current_user)
@bp.route('/awesome')
def awesome():
...
return render_template('awesome.html', user=current_user)
We can use a Flask Context Processor to always inject the current_user
object
from flask.ext.security import current_user
def user_context_processor():
if current_user.is_authenticated():
user = current_user._get_current_object()
else:
user = None
return {
'user': user,
}
def register_processors(app):
app.context_processor(user_context_processor)
We simplify our routes
@bp.route('/gnarly')
def gnarly():
...
return render_template('gnarly.html')
@bp.route('/tubular')
def tubular():
...
return render_template('tubular.html')
@bp.route('/way-cool')
def way_cool():
...
return render_template('way_cool.html')
@bp.route('/awesome')
def awesome():
...
return render_template('awesome.html')
and simplify usage in the template
{% if user %}
<div class="signup">
Welcome {{ user.full_name }}!
<a href="{{ url_for('profile', user) }}">(Profile)</a>
</div>
{% else %}
<div class="signup">
Welcome!
<a href="{{ url_for('signup') }}">Signup to join.</a>
</div>
{% endif %}
app.config
is a dict
like object with
<Config {
'JSON_AS_ASCII': True,
'USE_X_SENDFILE': False,
'SESSION_COOKIE_PATH': None,
'SESSION_COOKIE_DOMAIN': None,
'SESSION_COOKIE_NAME': 'session',
'LOGGER_NAME': '__main__',
'DEBUG': False,
'SECRET_KEY': None,
'MAX_CONTENT_LENGTH': None,
'APPLICATION_ROOT': None,
'SERVER_NAME': None,
'PREFERRED_URL_SCHEME': 'http',
'JSONIFY_PRETTYPRINT_REGULAR': True,
'TESTING': False,
'PERMANENT_SESSION_LIFETIME': datetime.timedelta(31),
'PROPAGATE_EXCEPTIONS': None,
'TRAP_BAD_REQUEST_ERRORS': False,
'JSON_SORT_KEYS': True,
'SESSION_COOKIE_HTTPONLY': True,
'SEND_FILE_MAX_AGE_DEFAULT': 43200,
'PRESERVE_CONTEXT_ON_EXCEPTION': None,
'SESSION_COOKIE_SECURE': False,
'TRAP_HTTP_EXCEPTIONS': False
}>
app.config
can also configure extensions!
class FlaskExtension(object):
def __init__(self, app=None):
if app:
self.init_app(app)
def init_app(self, app):
if not hasattr(app, 'extensions'):
app.extensions = {}
app.extensions['EXTENSION'] = self
self.active = app.config.get('EXTENSION_ACTIVE')
self.style = app.config.get('EXTENSION_STYLE')
Application Factories can become messy with config logic
def create_app(config=None, environment=None):
app = Flask(__name__)
app.config['ENVIRONMENT'] = environment
app.config.update(config or {})
if app['ENVIRONMENT'] in ('PRODUCTION', 'TESTING'):
app.config['EXTENSION_ACTIVE'] = True
else:
app.config['EXTENSION_ACTIVE'] = False
if app['ENVIRONMENT'] = 'TESTING':
app.config['EXTENTION_STYLE'] = 'testing'
if app['ENVIRONMENT'] = 'PRODUCTION':
app.config['SECRET_KEY'] = '27d4c2bc-0fac-4ea1-98e8-1ea1feba31f1'
app.config['SERVER_NAME'] = 'flask-special.com'
...
return app
Use the right tool for the job.
COMMON: &common
DEBUG: False
EXTENSION_ACTIVE: True
DEVELOPMENT: &development
<<: *common
EXTENSION_ACTIVE: False
TESTING: &testing
<<: *common
EXTENSION_STYLE: 'testing'
PRODUCTION: &production
<<: *common
SECRET_KEY: '27d4c2bc-0fac-4ea1-98e8-1ea1feba31f1'
SERVER_NAME: 'flask-special.com'
A library for managing complex configurations.
config.yml
< local_config.yml
< envExample Configuration
COMMON: &common
DEBUG: False
EXTENSION_ACTIVE: True
DEVELOPMENT: &development
<<: *common
EXTENSION_ACTIVE: False
SQLALCHEMY_URL: 'ordbok_local_config'
PRODUCTION: &production
<<: *common
SECRET_KEY: 'ordbok_private_config'
SQLALCHEMY_URL: 'ordbok_env_config_database_uri'
SERVER_NAME: 'flask-special.com'
Using with Flask
from ordbok.flask_helper import FlaskOrdbok
def create_app(config=None, environment=None):
app = Flask(__name__)
ordbok = FlaskOrdbok(app)
ordbok.load()
app.config.update(ordbok)
app.config.update(config or {})
...
return app
if __name__ == '__main__':
app = create_app(environment='DEVELOPMENT')
ordbok = app.extensions['ordbok']
ordbok.app_run(app)
ordbok.app_run(app)
will auto-reload on changes to yaml config, as well as changes to python source files.
from flask import current_app
class Extension(object):
def __init__(self, app=None):
self._app = app
if app:
return self.init_app(app)
def init_app(self, app):
if not hasattr(app, 'extensions'):
app.extensions = {}
app.extensions.update({'extension': self,})
@property
def app(self):
return self._app or current_app
def config(self, key, default=None):
key = 'EXTENSION_{}'.format(key.upper())
if default:
return self.app.config.get(key, default)
return self.app.config[key]
@property
def namespace(self):
return self.config('namespace', 'extension')
from flask import current_app, Blueprint
extension_bp = Blueprint('index', __name__, url_prefix='/ext')
@extension_bp.route('/', methods=('GET',))
def index():
return 'hello ext'
@extension_bp.route('/echo/<content>', methods=('GET',))
def echo(content):
return content
class Extension(object):
...
def init_app(self, app):
...
self.init_blueprints(app)
def init_blueprints(self, app):
extension_bp.name = '{}.index'.format(self.namespace)
extension_bp.url_prefix = '/{}'.format(self.namespace)
app.register_blueprint(extension_bp)
from flask import current_app
from flask.ext.sqlalchemy import sqlalchemy as sa
class ExtensionModelBase(object):
id = sa.Column(sa.Integer, primary_key=True)
content = sa.Column(sa.Text)
class Extension(object):
...
def init_app(self, app, db):
...
app.extensions.update({'extension': self, 'db': db})
self.init_models()
@property
def db(self):
return self.app.extensions['db']
def init_models(self):
class ExtensionModel(self.db.Model, ExtensionModelBase):
__tablename__ = '{}_model'.format(self.namespace)
Bare bones example at https://github.com/eriktaubeneck/flask-ext-test.