Class based views: Confused by the lifecycle

I’m currently experimenting with class based views, because I see options to simplify my code a lot. But I’m somewhat confused by the lifecycle of class base views. Lets start with a simple question:

Let say I have a class base view like

class MyView(View):
    # ...

In most examples it is used like this:

urlpatterns = [
    path("some_path/", MyView.as_view(), name="some_name"),
]

Assuming my view is able to handle multiple paths. Is there some reason that forbids or discourages to do this:

my_view = MyView.as_view()
urlpatterns = [
    path("some_path/", my_view, name="some_name"),
    path("other_path/", my_view, name="other_name"),
]

Is it ok to reuse the result of MyView.as_view() or should I call it once per path? The background of the question is that MyView needs to initialize itself. It needs to prepare some internal data structures at startup that will be static during runtime. I would prefer to do it just once. That’s the reason why I would like to reuse the object for multiple paths.

I expect as_view() to create an instance of MyView so I would do the initialization in __init__. But there is also setup which sounds like it should be use for such tasks. The docs say:

When the view is called during the request/response cycle, the setup() method assigns the HttpRequest to the view’s request attribute, and any positional and/or keyword arguments captured from the URL pattern to the args and kwargs attributes, respectively. Then dispatch() is called.

This suggests that setup and dispatch are always called as a pair one after another. They both seem to have the same signature and from the docs I’m not able to understand why there are two methods. Now I have the feeling that I completely misunderstood something, which leaves me confused. :wink:

Can somebody tell me where I got lost and where to run one-time initialization code of my view?

Nope, none at all.

There is no “physical” connection between a URL and a view, only a logical connection established by the urlpatterns.

You would need to override or replace the as_view method for this purpose. It’s that method that prepares the CBV for use on a per-request basis.

That doc you referenced kind of highlights this in the example:

response = MyView.as_view()(request)

The MyView.as_view() function call returns a function that is logically the same as an FBV. (It’s a function that accepts a request and returns a response.) What that function does is call setup followed by dispatch as you’ve identified.

I suggest you look at the as_view method of the View class to see what it’s doing.

I also suggest you become familiar with the Classy Class-Based Views site - I’ve found it to be a great tool to help understand how CBVs work.

2 Likes

Thanks! Reading the actually quite simple code of as_view() answered more or less all my questions. I should be more “fearless” reading the source code. :slight_smile:

For future readers, the generated view boils down more or less to the following code:

        def view(request, *args, **kwargs):
            self = cls(**initkwargs)
            self.setup(request, *args, **kwargs)
            # ...
            return self.dispatch(request, *args, **kwargs)

So there is a dedicated instance of MyView per request.

1 Like