Routing and View Logic
Routing in this codebase is managed through the Scaffold class, which provides the foundation for both the main Flask application and Blueprint objects. You can define routes using functional decorators or by implementing class-based views for more complex logic.
Functional Routing
The most common way to define routes is using the @app.route decorator (or @bp.route for blueprints). This associates a URL rule with a view function.
from flask import Blueprint, request, redirect, url_for, render_template
bp = Blueprint("blog", __name__)
@bp.route("/<int:id>/update", methods=("GET", "POST"))
def update(id):
# The 'id' variable is captured from the URL and passed as an argument
post = get_post(id)
if request.method == "POST":
title = request.form["title"]
# ... logic to update database ...
return redirect(url_for("blog.index"))
return render_template("blog/update.html", post=post)
In this example from examples/tutorial/flaskr/blog.py, the <int:id> syntax defines a URL parameter that is automatically converted to an integer and passed to the update function.
Class-Based Views
For views that share logic or follow a RESTful pattern, you can use class-based views defined in flask.views.
Generic View
The base View class requires you to implement the dispatch_request method. You register it using add_url_rule and the as_view method.
import flask.views
class MyView(flask.views.View):
def dispatch_request(self):
return "Hello from a class-based view!"
app.add_url_rule("/my-view", view_func=MyView.as_view("my_view_endpoint"))
MethodView for RESTful APIs
MethodView is a specialized subclass that automatically dispatches requests to methods named after HTTP verbs (e.g., get, post, delete).
import flask.views
class UserAPI(flask.views.MethodView):
def get(self, user_id):
if user_id is None:
# return a list of users
pass
else:
# expose a single user
pass
def post(self):
# create a new user
pass
view = UserAPI.as_view("user_api")
app.add_url_rule("/users/", defaults={"user_id": None}, view_func=view, methods=["GET"])
app.add_url_rule("/users/", view_func=view, methods=["POST"])
app.add_url_rule("/users/<int:user_id>", view_func=view, methods=["GET", "PUT", "DELETE"])
As seen in tests/test_views.py, MethodView automatically populates the allowed methods for the route based on the methods defined in the class.
Advanced View Configuration
Applying Decorators
When using class-based views, you cannot use standard decorators on the class itself. Instead, define a decorators attribute containing a list of decorators to apply to the generated view function.
def login_required(f):
# ... implementation ...
return f
class ProfileView(flask.views.View):
decorators = [login_required]
def dispatch_request(self):
return "Secret Profile"
Controlling Instance Lifecycle
By default, a new instance of the view class is created for every request. If your view is expensive to initialize and does not store request-specific state on self, you can set init_every_request = False.
class StaticDataView(flask.views.View):
init_every_request = False
def __init__(self):
# This runs only once when the view is first accessed
self.data = load_expensive_data()
def dispatch_request(self):
return self.data
Warning: If
init_every_requestisFalse, you must not store any request-specific data onself, as the instance is shared across multiple requests. Useflask.gfor request-bound data instead.
Error Handling
You can register custom error handlers for specific HTTP status codes or exception types using the @app.errorhandler decorator.
@app.errorhandler(404)
def page_not_found(error):
return render_template("errors/404.html"), 404
@app.errorhandler(DatabaseError)
def handle_db_error(error):
return "A database error occurred", 500
Blueprints also support error handlers via @bp.errorhandler, which only trigger for requests handled by that specific blueprint.
Request Lifecycle Hooks
The Scaffold class provides methods to hook into the request lifecycle. These are available on both Flask and Blueprint objects.
before_request: Runs before the view function. If it returns a value, that value is used as the response and the view is skipped.after_request: Runs after the view function. It receives the response object and must return a response object.teardown_request: Runs after the response is sent, even if an unhandled exception occurred.
@app.before_request
def load_user():
if "user_id" in session:
g.user = db.session.get(session["user_id"])
else:
g.user = None
@app.after_request
def add_header(response):
response.headers["X-Content-Type-Options"] = "nosniff"
return response
These hooks allow you to manage resources like database connections or perform global response modifications without repeating logic in every view.