Opened 6 years ago

Closed 6 years ago

Last modified 6 years ago

#28943 closed Cleanup/optimization (worksforme)

Avoid the need to call get_context_data() in TemplateView subclasses

Reported by: James Pic Owned by: nobody
Component: Generic views Version: 2.0
Severity: Normal Keywords:
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description (last modified by James Pic)

Currently, TemplateView inherits render_to_response(context) from TemplateResponseMixin which requires a context argument.

This means that when your TemplateView subclass wants to return the TemplateResponse with the default context, you still have to create and pass the default context:

class YourView(TemplateView):
    def post(self, request, *a, **k):
        if not self.dostuff():
            return http.HttpResponseBadRequest()

        context = self.get_context_data(*k)
        return self.render_to_response(context)

The reason for this is that ContentMixin defines get_context_data(), and TemplateResponseMixin defines render_to_response(context, ...), TemplateResponse mixes the two in get():

class TemplateView(TemplateResponseMixin, ContextMixin, View):
    """
    Render a template. Pass keyword arguments from the URLconf to the context.
    """
    def get(self, request, *args, **kwargs):
        context = self.get_context_data(**kwargs)
        return self.render_to_response(context)

I think it would be more usable as such:

class TemplateView(TemplateResponseMixin, ContextMixin, View):
    """
    Render a template. Pass keyword arguments from the URLconf to the context.
    """
    def get(self, request, *args, **kwargs):
        return self.render_to_response(**kwargs)

    def render_to_response(self, context=None, **kwargs):
        context = context or self.get_context_data(**kwargs)
        return self.render_to_response(context)

Then, users could call render_to_response() in their code without dealing with a context they don't override because they are satisfied with the default (which adds view=self in the context), ie:

class YourView(TemplateView):
    def get(self, request, *a, **k):
        self.token = self.generatetoken()
        return self.render_to_response(**k)

    def post(self, request, *a, **k):
        if not self.dostuff():
            return http.HttpResponseBadRequest()
        return self.render_to_response(**k)

Change History (16)

comment:1 by James Pic, 6 years ago

Description: modified (diff)

comment:2 by James Pic, 6 years ago

Description: modified (diff)

comment:3 by Tim Graham, 6 years ago

Easy pickings: unset

The ticket summary is cryptic. Please describe the use case in more detail. I don't see an indication of where TemplateResponse.get(self, request, *args, **kwargs) lives.

comment:4 by James Pic, 6 years ago

Description: modified (diff)

comment:5 by James Pic, 6 years ago

Thanks for your feedback, I rewrote the issue, does it make any sense now ?

comment:6 by James Pic, 6 years ago

Description: modified (diff)

comment:7 by James Pic, 6 years ago

Description: modified (diff)

comment:8 by James Pic, 6 years ago

Description: modified (diff)

comment:9 by James Pic, 6 years ago

Description: modified (diff)

comment:10 by James Pic, 6 years ago

Description: modified (diff)

comment:11 by Tim Graham, 6 years ago

Summary: Unenforce manual get_context_data()Avoid the need to call get_context_data() in TemplateView subclasses

Is there a reason to duplicate the render_to_response() call in post()? I didn't test it, but I believe you could write both examples as:

class YourView(TemplateView):
    def post(self, request, *a, **k):
        if not self.dostuff():
            return http.HttpResponseBadRequest()

        return super().get(request, *a, **k)

comment:12 by Tim Graham, 6 years ago

Resolution: worksforme
Status: newclosed

comment:13 by James Pic, 6 years ago

Of course, that works, unless you've overridden get() to do things that you don't want to happen on post.

In post, i would like to render to template again, not run get() ;)

comment:14 by Tim Graham, 6 years ago

Unless I missed something, super().get() will call the TemplateView implementation, even if you override get() in a subclass.

comment:15 by James Pic, 6 years ago

Yes, returning super().get() allows to bypass the last level of get() override and i think this had a side effect in some code i'm trying to remember.

Last edited 6 years ago by James Pic (previous) (diff)

comment:16 by James Pic, 6 years ago

But after considering every option it seems like adding a comment in code would be even useless for this I think an interresting story brought this issue but there's nothing to fix in django so TemplateView.get works for me very well by the way that's actually what i already have actually, sry for the noise <3

Last edited 6 years ago by James Pic (previous) (diff)
Note: See TracTickets for help on using tickets.
Back to Top