Returned a template response with no `template_name` attribute set on either the view or response (drf-yasg2)

2.2k Views Asked by At

I setup everything as per the docs of drf-yasg2 but I'm getting this error and I can't find whats causing it. I went through the docs and there is nothing I can use from there. It looks like every response is falling back to the TemplateHTMLRenderer instead of using drf-yasg2 ReDoc or SwaggerUI renderers.

    def render(self, swagger, accepted_media_type=None, renderer_context=None):
        if not isinstance(swagger, Swagger):  # pragma: no cover
            return TemplateHTMLRenderer().render(
                swagger, accepted_media_type, renderer_context
            )

        self.set_context(renderer_context, swagger)
        return render_to_string(
            self.template, renderer_context, renderer_context["request"]
        )

This condition will never return false

if not isinstance(swagger, Swagger):

because the Response class from djangorestgramework (response.py) is calling this method with the self.data instead of a rendered as a parameter:

ret = renderer.render(self.data, accepted_media_type, context)

I have no idea what I'm doing wrong here.

In my project I use:

  • drf-yasg2==1.18.5
  • djangorestframework==3.8.0
  • Django==2.0.1

urls.py

from rest_framework import permissions
from drf_yasg2.views import get_schema_view
from drf_yasg2 import openapi

schema_view = get_schema_view(
   openapi.Info(
      title="Snippets API",
      default_version='v1',
      description="Test description",
   ),
   public=True,
   permission_classes=(permissions.AllowAny,),
)

urlpatterns = [
    
    url(r'^swagger(?P<format>\.json|\.yaml)$', schema_view.without_ui(cache_timeout=0), name='schema-json'),
    url(r'^swagger/$', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
    path('redoc/', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'),

settings.py -> INSTALLED_APPS

INSTALLED_APPS = [
    'rest_framework',
    'myappname',
    'rest_framework.authtoken',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'fcm_django',
    'storages',
    'colorfield',
    'drf_yasg2',
]

The error

Internal Server Error: /swagger/
Traceback (most recent call last):
  File "C:\Users\User\AppData\Local\Programs\Python\Python37\lib\site-packages\django\core\handlers\exception.py", line 35, in inner
    response = get_response(request)
  File "C:\Users\User\AppData\Local\Programs\Python\Python37\lib\site-packages\django\core\handlers\base.py", line 158, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "C:\Users\User\AppData\Local\Programs\Python\Python37\lib\site-packages\django\core\handlers\base.py", line 156, in _get_response
    response = response.render()
  File "C:\Users\User\AppData\Local\Programs\Python\Python37\lib\site-packages\django\template\response.py", line 106, in render
    self.content = self.rendered_content
  File "C:\Users\User\AppData\Local\Programs\Python\Python37\lib\site-packages\rest_framework\response.py", line 72, in rendered_content
    ret = renderer.render(self.data, accepted_media_type, context)
  File "C:\Users\User\AppData\Local\Programs\Python\Python37\lib\site-packages\drf_yasg2\renderers.py", line 79, in render
    swagger, accepted_media_type, renderer_context
  File "C:\Users\User\AppData\Local\Programs\Python\Python37\lib\site-packages\rest_framework\renderers.py", line 168, in render
    template_names = self.get_template_names(response, view)
  File "C:\Users\User\AppData\Local\Programs\Python\Python37\lib\site-packages\rest_framework\renderers.py", line 197, in get_template_names
    'Returned a template response with no `template_name` attribute set on either the view or response'
django.core.exceptions.ImproperlyConfigured: Returned a template response with no `template_name` attribute set on either the view or response
[16/Oct/2020 00:39:23] "GET /swagger/ HTTP/1.1" 500 112354
4

There are 4 best solutions below

0
On

If you have a ViewSet, what helped me was adding a template_name.

class QuestionViewSet(viewsets.ModelViewSet):
    queryset = Question.objects.filter()
    serializer_class = QuestionSerializer
    permission_classes = [permissions.IsAuthenticatedOrReadOnly,
                          IsOwnerOrReadOnly]
    renderer_classes = [JSONRenderer,renderers.TemplateHTMLRenderer]
    template_name = 'polls/index.html'
0
On

For me the problem came up when working with a custom route added by @action decorator. The solution was to use renderer_classes=[JSONRenderer, ] in the definition i.e.

from rest_framework.renderers import JSONRenderer
from rest_framework.response import Response

    @action(methods=['get'], detail=False, url_path='feed/(?P<id>[^/]+)', url_name='list-feed', schema=feed_schema,
        renderer_classes=[JSONRenderer, ])
def feed(self, request, *args, **kwargs):
    return Response({})

More on renderer_classes here.

1
On

The answer might come late and might not be the best one, but what you could do is put ?format=json at the end of your URL.

So for example - 127.0.0.1:8000/v1/api/user/random123?format=json.

0
On

On a GET request returning JSON, managed to get around this by adding the following decorators above the function name in views.py:

@api_view(('GET',))
@renderer_classes((JSONRenderer,))

You'll need to import these for the decorators to work:

from rest_framework.decorators import api_view, renderer_classes
from rest_framework.renderers import JSONRenderer