Your First Class-Based View
Class-based views provide an alternative way to implement view logic as classes instead of functions. This approach promotes code reuse through inheritance and allows you to customize view behavior using class attributes.
In this tutorial, you will build a reusable RenderTemplateView that can be used to serve multiple pages (like "About" or "Contact") by passing different configuration arguments to the same class.
Prerequisites
To follow this tutorial, you need a basic Flask application instance:
from flask import Flask, render_template
from flask.views import View
app = Flask(__name__)
Step 1: Subclassing the View Base Class
The foundation of any class-based view is the View class found in src.flask.views. To create your own view, you must subclass it and implement the dispatch_request method.
First, define a class that accepts a template name in its constructor:
class RenderTemplateView(View):
def __init__(self, template_name: str) -> None:
self.template_name = template_name
def dispatch_request(self) -> str:
return render_template(self.template_name)
The dispatch_request method is where the actual view logic resides. It acts like a standard function-based view, returning a response string, a response object, or a tuple.
Step 2: Registering the View with as_view
You cannot pass the class itself to app.add_url_rule. Instead, you must use the as_view class method to convert the class into a view function.
The first argument to as_view is the name of the endpoint. Any additional arguments passed to as_view are forwarded to the class's __init__ method.
app.add_url_rule(
"/about",
view_func=RenderTemplateView.as_view("about_page", template_name="about.html"),
)
app.add_url_rule(
"/contact",
view_func=RenderTemplateView.as_view("contact_page", template_name="contact.html"),
)
By using as_view, you have created two different endpoints using the same logic but different configurations.
Step 3: Adding Allowed Methods and Decorators
By default, a View only responds to GET requests. You can change this by setting the methods class attribute. Additionally, if you need to apply middleware (like authentication), use the decorators attribute.
def add_x_parachute(f):
"""A sample decorator that adds a custom header to the response."""
def new_function(*args, **kwargs):
from flask import make_response
resp = make_response(f(*args, **kwargs))
resp.headers["X-Parachute"] = "awesome"
return resp
return new_function
class Index(View):
methods = ["GET", "POST"]
decorators = [add_x_parachute]
def dispatch_request(self):
return "Awesome"
app.add_url_rule("/", view_func=Index.as_view("index"))
Note: Do not use the @decorator syntax on the class itself; it will not be applied to the generated view function. Always use the decorators list attribute.
Step 4: Optimizing with init_every_request
By default, Flask creates a new instance of your view class for every single request. This is safe but can be inefficient if your __init__ method performs expensive setup.
If your view does not store request-specific data on self, you can set init_every_request to False. This tells Flask to create the instance once and reuse it.
class CountInit(View):
init_every_request = False
def __init__(self):
self.count = 0
print("Initialized once!")
def dispatch_request(self):
self.count += 1
return f"Request count: {self.count}"
app.add_url_rule("/count", view_func=CountInit.as_view("counter"))
Warning: When init_every_request is False, the same instance is shared across threads. You must not store request-specific data (like the current user) on self. Use flask.g for that instead.
Complete Working Result
Combining these concepts, here is a complete example of a reusable, optimized class-based view:
from flask import Flask, render_template
from flask.views import View
app = Flask(__name__)
class RenderTemplateView(View):
init_every_request = False
methods = ["GET"]
def __init__(self, template_name: str) -> None:
self.template_name = template_name
def dispatch_request(self) -> str:
return render_template(self.template_name)
# Registering multiple routes with the same class
app.add_url_rule(
"/about",
view_func=RenderTemplateView.as_view("about", template_name="about.html")
)
app.add_url_rule(
"/terms",
view_func=RenderTemplateView.as_view("terms", template_name="terms.html")
)
Next Steps
If you are building a REST API where you want to handle different HTTP methods (GET, POST, DELETE) in different functions, look into MethodView, which automates method dispatching based on function names.