counsyl / django-pedant

Make django templates fail fast
MIT License
13 stars 3 forks source link

Unable to get fail_on_template_errors working with class based views #5

Open clokep opened 8 years ago

clokep commented 8 years ago

I've been unable to get this to work with class based views (well without calling render_to_string manually in a CBV, which kind of defeats the purpose).

I was hoping to do something like:

from pedant.decorators import fail_on_template_errors

class MyView(TemplateView):
    template_name = 'my_view.html'

    @fail_on_template_errors
    def render_to_response(self, *args, **kwargs):
        return super(MyView, self).render_to_response(*args, **kwargs)

I don't see how the TemplateResponse code differs much from render_to_string, but I've been unable to get this to work. Is there a fundamental flaw that this doesn't work on CBVs or am I doing something wrong?

Thanks!

(If this worked, it might make a nice mixin to include, or at least document!)

lucaswiman commented 8 years ago

@clokep Could you post the stack trace and also the Django version you're using?

clokep commented 8 years ago

@lucaswiman There's no stack trace (that's the problem :wink:).

I'm on Django 1.9.6.

My template:

This is a template {{ foo }}

The view:

from pedant.decorators import fail_on_template_errors

class MyView(TemplateView):
    template_name = 'test.html'

    @fail_on_template_errors
    def render_to_response(self, *args, **kwargs):
        return super(MyView, self).render_to_response(*args, **kwargs)

The result is that I see a page that says "This is a template ", I expected an exception.

lucaswiman commented 8 years ago

As far as I can tell, the issue is that class-based views use a lazy-rendering TemplateResponse object, which (despite the name) isn't actually rendered in render_to_response. I think you'd need to subclass TemplateResponse and override its .render() method, then set that as the response_class:

from django.template.response import *
class PedanticTemplateResponse(TemplateResponse):
    @fail_on_template_errors
    def render(self, *args, **kwargs):
        return super(PedanticTemplateResponse, self).render(self, *args, **kwargs)

class MyView(TemplateView):
    response_class = PedanticTemplateResponse

If you want to add this or documentation for using CBVs, pull requests are definitely welcome. Personally, little gotchas like this are why I try to avoid CBVs.

clokep commented 8 years ago

Thanks for the hints! I forgot templates get rendered in middleware.

A less clean solution that requires less overwriting would be:

from pedant.decorators import fail_on_template_errors

class MyView(TemplateView):
    template_name = 'test.html'

    @fail_on_template_errors
    def render_to_response(self, *args, **kwargs):
        result = super(MyView, self).render_to_response(*args, **kwargs)
        result.render()
        return result

There's a minor bug in your code, by the way, render shouldn't have self as an argument.

I'll try to put up a PR for this soon!