[PYTHON] About Flask customization

2012 Python Advent Calendar (Web Framework) I am in charge of the 5th day of #python_adv. This article will be a multipost with my blog. http://methane.hatenablog.jp/entry/2012/12/05/134206

Flask is not a toy

IDEs such as PyCharm have begun to support Flask, and Flask is becoming more and more popular as Python's web framework No. 2 following Django. (Although Python3 support is slow for that)

Speaking of Flask, it is famous for the person who can easily write the micro framework Hello World.

hello.py


import flask
app = flask.Flask(__name__)
@app.route('/')
def index():
    return "Hello, World."
app.run(debug=True)

Looking at this, it looks like a toy, but Flask has a solid foundation that allows you to put multiple apps on one Python interpreter, or multiple instances of one app, and it is a module using Blueprint. It also has a framework that can be used for large-scale applications.

Compared to the full stack framework, the point that libraries such as OR mapper, form library and AJAX support library are not built in is micro, but if you want to choose such a library yourself, modify the full stack framework I think it's often easier to extend Flask than to do it.

Today I'll give you a quick overview of how to customize Flask for each app. (It's time to create a modular Flask extension like Flask-SQLAlchemy)

Customization pattern

You can inherit and customize the Flask class, or you can use the methods of the Flask instance, but in most cases the latter will suffice.

There are a lot of functions for customization, but there aren't many patterns for how to extend them, so I'll show you three patterns.

Modify app attributes directly

For example, if you prepare variables used in the header part by default for the entire application, it is convenient because you do not have to set variables in render_response every time. Flask provides an extension that overrides create_jinja_environment, but it's easier to add it directly at this level.

import random
app.jinja_env.globals['random'] = random.randint

However, be aware that jinja_env will be created using create_jinja_environment the first time it is accessed, so the timing will be accelerated. I think it's okay in most cases, but if necessary, use the decorator-style extension method described below.

@app.before_first_request
def extend_jinja_env():
    app.jinja_env.globals['random'] = random.randint

It's a good idea to delay until the first request arrives, or override create_jinja_environment normally.

Since there is only one jinja environment for each application, variables cannot be added for each Blueprint. In that case, write the render function in the module that makes the blueprint.

def render(tmpl, **kw):
    kw['rnd'] = random.randrange(1000)
    return flask.render_response(tmpl, **kw)

An example of using the method of directly modifying the attributes of app is to replace session_interface and use your own session handler.

PS: I was able to add variables using Flask's and Blueprint's context_processor decorators. It is also possible for each Blueprint. This is a good way to store different values for each request.

http://flask.pocoo.org/docs/templating/#context-processors

Expansion with decorators

If you want to display the datetime with +0 Timezone as Japan time in the template, such as when the time is stored in UTC in the database though it is an application for Japan, you can add a filter like this.

@app.template_filter()
def jptime(dt, format='%Y-%m-%d %H:%M:%S'):
    u"""Convert utc time to a string in the format specified in Japan time."""
    local = dt + datetime.timedelta(hours=9)
    return local.strftime(format)

It looks like this when using it.

Last Modified: {{ post.last_modified | jptime('%m/%d %H:%M') }}

@ app.template_filter is not just a decorator, it's a function-style decorator, but you can write name =" JT " in this argument to separate the function name from the filter name in the template. ..

There are many other extensions that use decorators. Most of them are also available in Blueprint. For example, suppose a Blueprint requires authentication for all requests.

from myapp.auth import auth
bp = flask.Blueprint('admin', __name__)

@bp.route('/')
@auth.required
def index():
    #…

@bp.route('/profile')
@auth.required
def profile():
    #…

If you register a function with before_request, it will be called before calling the view function. In addition, the view function call is skipped when this function returns a response, so you can use the @ auth.required decorator for the view function.

bp = flask.Blueprint('admin', __name__)

@bp.before_request
@auth.required
def before_request():
    flask.g.user = auth.get_user()

#…

There are two types of Flask extension decorators, functional and just decorators, but don't worry, you'll definitely get an exception if you use it incorrectly, and you can see it in one shot by looking at the stack trace.

Use app context / request context

Information about the currently running application and information about the request currently being processed, such as flask.current_app and flask.request, are managed using a thread-local stack called the context stack. The purpose of the stack is to allow you to run other Flask apps from within the Flask app, and if you want to extend an app, edit the context at the top of the stack.

For example, suppose you want to connect to the DB for each request and disconnect at the end.

In this case, the request context is fine, but the app context, which can be easily used even from a script without an HTTP request, is more suitable.

def _connect_db():
    return MySQLdb(**app.config['MYSQL_OPTS'])

def get_db():
    top = flask._app_context_stack.top
    if top is None:
        raise RuntimeError('working outside of application context')
    if not hasattr(top, 'db'):
        top.db = _connect_db()
    return top.db

@app.teardown_appcontext
def _close_db():
    top = flask._app_context_stack.top
    if top is None:
        raise RuntimeError('working outside of application context')
    if hasattr(top, 'db'):
        top.db.close()
        top.db = None

If you want to make it just a global variable db instead of calling get_db () every time, use LocalProxy.

from werkzeug.local import LocalProxy
db = LocalProxy(get_db)

However, LocalProxy has some overhead, so you may not want to use it if you frequently access attributes hundreds of times in one request.

In practice, it's easier to use the variable flask.g on the request context without having to change the context directly. (Example: http://flask.pocoo.org/docs/patterns/sqlite3/)

I think it's enough to deal directly with the context only if you want to be reusable independently of your own application, such as when creating a Flask extension.

Don't use flask.session

Flask sessions can be customized by replacing Flask.session_interface.

This customization is easy enough, but since it is called before before_request, for example, if you want to generate a session ID associated with a user ID in an application that requires login, you have to generate a session object before authentication, or Blueprint It is troublesome when you want to control the handling of sessions in detail on a unit basis. So here's how to disable flask.session.

app.session_interface = flask.sessions.NullSessionInterface()

After that, if you put your own session in flask.g.session etc. and define the behavior using before_request, teardown_request etc., you can control the session more freely. For example, to use Beaker with Flask, refer to http://flask.pocoo.org/snippets/61/ and customize it as needed.

Recommended Posts

About Flask customization
flask
flask
About parameter processing in Flask request.args
About LangID
About CAGR
About virtiofs
About python-apt
About sklearn.preprocessing.Imputer
About gunicorn
About requirements.txt
First Flask
About permissions
About Opencv ②
Flask memo
About axis = 0, axis = 1
About Opencv ③
About import
About numpy
About pip
About Linux
About numpy.newaxis
About endian
About Linux
About import
About Opencv ①
About Linux
About Linux
About Linux ①
About cv2.imread
About _ and __
About wxPython