View Lifecycle and Performance
When you define a class-based view in Flask using the View or MethodView classes, the lifecycle of the view instance is governed by the init_every_request attribute. This setting determines whether Flask creates a fresh instance of your class for every incoming request or reuses a single instance for the lifetime of the application.
Per-Request Instantiation (Default)
By default, View.init_every_request is set to True. In this mode, the view function generated by as_view() acts as a factory. Every time a request matches the route, Flask instantiates the class and calls its dispatch_request method.
This behavior is implemented in src/flask/views.py within the as_view method:
if cls.init_every_request:
def view(**kwargs: t.Any) -> ft.ResponseReturnValue:
self = view.view_class( # type: ignore[attr-defined]
*class_args, **class_kwargs
)
return current_app.ensure_sync(self.dispatch_request)(**kwargs)
The primary advantage of this approach is safety. Because a new instance is created for every request, you can safely store request-specific data as attributes on self without worrying about data leaking between different users or concurrent requests.
Singleton Optimization
For views that perform expensive setup operations—such as loading a machine learning model, establishing a persistent connection, or processing large configuration objects—instantiating the class on every request can introduce significant latency.
By setting init_every_request = False, you instruct Flask to instantiate the class only once, when as_view() is first called. The resulting instance is then captured in a closure and reused for all subsequent requests:
else:
self = cls(*class_args, **class_kwargs) # pyright: ignore
def view(**kwargs: t.Any) -> ft.ResponseReturnValue:
return current_app.ensure_sync(self.dispatch_request)(**kwargs)
This pattern is demonstrated in tests/test_views.py, where a view tracks how many times it has been initialized:
class CountInit(flask.views.View):
init_every_request = False
def __init__(self):
nonlocal n
n += 1
def dispatch_request(self):
return str(n)
app.add_url_rule("/", view_func=CountInit.as_view("index"))
In this example, even after multiple requests to /, the value of n remains 1 because __init__ was only executed once during the registration of the route.
State Management and Thread Safety
The performance gain of the singleton pattern comes with a critical constraint: thread safety.
When init_every_request is False, the same instance is shared across all requests handled by a worker process. If your application uses a multi-threaded server (like the default Flask development server), multiple threads will call dispatch_request on the same instance simultaneously.
- In Default Mode (
True): You can safely useself.user_id = ...to store data for the duration of the request. - In Singleton Mode (
False): You must never store request-specific data onself. Instead, useflask.gto store data that should be local to the current request context.
Initialization Arguments
The as_view method accepts arbitrary arguments and keyword arguments after the required name parameter. These are forwarded to the class's __init__ method.
- If
init_every_requestisTrue, these arguments are stored and passed to the constructor every time a request arrives. - If
init_every_requestisFalse, these arguments are used immediately to create the singleton instance.
This allows you to customize class-based views at the time of registration:
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", template_name="about.html")
)
Execution Context
Regardless of the lifecycle choice, Flask wraps the call to dispatch_request with current_app.ensure_sync. This ensures that Flask can correctly handle both standard synchronous methods and async def dispatch_request implementations, as seen in tests/test_async.py:
class AsyncView(View):
async def dispatch_request(self):
await asyncio.sleep(0)
return request.method
This integration allows class-based views to benefit from asynchronous execution while still adhering to the lifecycle rules defined by init_every_request.