Routing & Class-Based Views
Mapping URLs to logic in Flask is primarily handled through decorators on the application or blueprint instance, or by using class-based views for more complex, reusable logic.
Decorator-Based Routing
The most common way to define routes is using the @app.route decorator or its method-specific shortcuts like @app.get and @app.post. These are defined in flask.sansio.scaffold.Scaffold, which both Flask and Blueprint inherit from.
from flask import Flask, request, render_template, redirect, url_for
app = Flask(__name__)
@app.route("/")
def index():
return "Index Page"
@app.get("/login")
def login_get():
return render_template("login.html")
@app.post("/login")
def login_post():
# Logic to handle login
return redirect(url_for("index"))
Variable Rules
You can make parts of the URL dynamic by using the <converter:variable_name> syntax. Flask passes these variables as keyword arguments to the view function.
# From examples/tutorial/flaskr/blog.py
@app.route("/<int:id>/update", methods=("GET", "POST"))
def update(id):
# 'id' is passed from the URL as an integer
post = get_post(id)
if request.method == "POST":
# ... update logic ...
return redirect(url_for("blog.index"))
return render_template("blog/update.html", post=post)
Class-Based Views
For logic that is better organized as an object, Flask provides the View and MethodView classes in flask.views. These allow you to use inheritance and organize code by HTTP method.
Method-Based Dispatching with MethodView
MethodView automatically dispatches requests to methods named after the HTTP verbs (get, post, put, etc.). This is ideal for RESTful APIs.
# Based on tests/test_views.py
from flask.views import MethodView
class UserAPI(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
# Register the view using add_url_rule
user_view = UserAPI.as_view("user_api")
app.add_url_rule("/users/", defaults={"user_id": None}, view_func=user_view, methods=["GET"])
app.add_url_rule("/users/", view_func=user_view, methods=["POST"])
app.add_url_rule("/users/<int:user_id>", view_func=user_view, methods=["GET", "PUT", "DELETE"])
Generic Views with View
If you need a class-based view that doesn't follow the method-per-verb pattern, subclass flask.views.View and implement dispatch_request.
from flask.views import View
class RenderTemplateView(View):
def __init__(self, template_name):
self.template_name = template_name
def dispatch_request(self):
return render_template(self.template_name)
app.add_url_rule("/about", view_func=RenderTemplateView.as_view("about", "about.html"))
Applying Decorators to Class-Based Views
Because the @decorator syntax on a class applies to the class itself and not the view function generated by as_view, Flask provides a decorators class attribute.
# From tests/test_views.py
def login_required(f):
def wrapper(*args, **kwargs):
# ... auth logic ...
return f(*args, **kwargs)
return wrapper
class ProfileView(View):
decorators = [login_required]
def dispatch_request(self):
return "User Profile"
app.add_url_rule("/profile", view_func=ProfileView.as_view("profile"))
Optimization and Behavior
Instance Persistence
By default, Flask creates a new instance of your view class for every request. If your class has expensive initialization that doesn't depend on the request, set init_every_request = False.
class EfficientView(View):
init_every_request = False
def __init__(self):
# This runs only once when as_view is called
self.data = load_expensive_resource()
def dispatch_request(self):
return self.data
Automatic OPTIONS Handling
Flask automatically handles OPTIONS requests for your routes. You can disable this or customize it using the provide_automatic_options attribute in your view class.
class CustomOptionsView(View):
provide_automatic_options = False
def dispatch_request(self):
if request.method == "OPTIONS":
return "", {"Allow": "GET, POST"}
return "Hello"
Troubleshooting
Decorators Not Working
If you apply a decorator to a View subclass using standard Python syntax (@my_decorator), it will not wrap the actual request handling logic. Always use the decorators list attribute:
# WRONG: This decorates the class, not the view function
@login_required
class MyView(View):
...
# CORRECT: This ensures the decorator is applied to the view function returned by as_view()
class MyView(View):
decorators = [login_required]
...
Missing HTTP Methods
When using MethodView, Flask automatically populates the methods attribute based on the methods you define (e.g., get, post). If you define a get method but not a head method, Flask will automatically handle HEAD requests by calling your get method and stripping the body.