35

I trying out Django's class based views (CBVs).

class BlahView(TemplateView):
    template_name = 'blah/blah.html'
    def get_context_data(self, **kwargs):
        #code...

    def get(self, request, **kwargs):
        #more code...

Now, I know that I can get the request params from self.request. Now say I want to parse these request params and store them within the class. Can I store those in self.xxx? Now, obviously based on how classes work, this seems straightforward.

But I can't make out the flow of control, looking at the definition of View (superclass of TemplateView). The source mentions as_view() to be the 'entry-point'

I thought of setting my instance variables at the beginning of get_context_data() but that doesn't seem right to do initialization there.

Can I define an __init__() for my CBV? If so, will there be threading issues or something where multiple page-accesses possibly work with a global instance of my parsed data?

I know this sounds a bit messy, but I'm just a bit confused with the code flow in CBVs.

jpic
  • 32,891
  • 5
  • 112
  • 113

2 Answers2

59

According to the source of django.views.generic.base.View.as_view:

  • on django startup, as_view() returns a function view, which is not called
  • on request, view() is called, it instantiates the class and calls dispatch()
  • the class instance is thread safe

According to the source of django.views.generic.base.View.__init__, the request object is out of scope at this point so you can't parse it in your own constructor overload.

However, you could parse the request and set class view instance attributes in an overload of django.views.generic.base.View.dispatch, this is safe according to the source:

class YourView(SomeView):
    def dispatch(self, request, *args, **kwargs):
        # parse the request here ie.
        self.foo = request.GET.get('foo', False)

        # call the view
        return super(YourView, self).dispatch(request, *args, **kwargs)
jpic
  • 32,891
  • 5
  • 112
  • 113
  • Thanks for the event run-through :) It seems a bit yucky to do initialization anywhere other than an __init__... and it might seem a bit weird when others will work on my code, but I guess CBV design forces our hand. – ForeverLearnNeverMaster Jul 12 '12 at 10:03
  • 6
    Note that the [Django CBV docs](https://docs.djangoproject.com/en/1.3/ref/class-based-views/#generic-views) explicitly say that storing state variables on the instance is thread safe, so we're not just relying on our reading of the source code. – Alasdair Jul 12 '12 at 12:31
  • 1
    It doesn't force your hand: you could override as_view() and change the wrapper logic, so that it pass request to __init__. – jpic Jul 12 '12 at 12:32
  • @Alasdair: Damn! How did I miss that! Thanks for pointing it out. – ForeverLearnNeverMaster Jul 13 '12 at 06:23
  • 1
    It might be a better idea to use `kwargs` instead of `GET` as you can get anything in GET, and kwargs are filtered by regular expressions. – zeroos Jul 09 '14 at 09:26
  • I think it is still ok to initialize something in ctor, like self.object = None in the view that has SingleObjectMixin – Aleksei Petrenko Feb 26 '15 at 23:26
  • also by: `kwargs.get("foo", "foo_default")` – Evhz May 20 '16 at 01:09
2

@jpic provided a great answer. Inspired from it, I would like to reference the following blog post where the author claims that:

... We cannot override view, as doing so would require overriding as_view(). Overriding dispatch() is appealing (and what I did originally when I presented this talk) because it offers a single simple place to do so, but this defies the logic of dispatch(). Instead, it is best to call set_account() in overrides of both get() and post(). ...

Therefore, one can override the get or post methods and set any self.whatever variables. It feels somehow cleaner.

raratiru
  • 8,748
  • 4
  • 73
  • 113