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
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)
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.
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
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.
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.
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