Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Override accepted renderer in django-rest-framework on exception

I'm using django-rest-framework to create an endpoint which returns a PDF. However, when there is an error rendering the PDF, I want to return a JSON response. But DRF passes the data for the exception to my PDFRenderer class. How can I use JSONRenderer instead, only if there is an error?

class PDFRenderer(BaseRenderer):
    """ DRF renderer for PDF binary content. """
    media_type = 'application/pdf'
    format = 'pdf'
    charset = None
    render_style = 'binary'

    def render(self, data, media_type=None, renderer_context=None):
        return bytes(data)

For example, when my view says raise PermissionDenied(), because the authorized user does not have permission to view the requested PDF, DRF passes {'detail': 'You do not have permission to perform this action.'} as the data argument to PDFRenderer.render.

Edit: I tried a custom exception handler but apparently you still have to run it through DRF's exception handler as well, which passes it to the PDFRenderer.

like image 387
katy lavallee Avatar asked Aug 04 '17 20:08

katy lavallee


2 Answers

I found a way to do this with a custom exception handler after all:

from rest_framework.renderers import JSONRenderer
from rest_framework.views import exception_handler


def custom_exception_handler(exc, context):
    """ Switch from PDFRenderer to JSONRenderer for exceptions """
    if context['request'].accepted_renderer.format == 'pdf':
        context['request'].accepted_renderer = JSONRenderer()
    return exception_handler(exc, context)

It feels pretty hacky. Still hoping for a better answer.

like image 189
katy lavallee Avatar answered Oct 24 '22 06:10

katy lavallee


If request is ok and file exists, I read it as bytes and pass it to Response, as custom renderer I use PDFRenderer as yours. If request isn't ok, I return json. My solution is to check if data is instance of bytes, if not, I just return bytes from valid json string, because render method should return bytes anyway

class PDFRenderer(BaseRenderer):
    media_type = 'application/pdf'
    format = 'pdf'
    charset = None
    render_style = 'binary'

    def render(self, data, media_type=None, renderer_context=None):
        if isinstance(data, bytes):
            return data
        data_to_response = json.dumps(data)
        return bytes(data_to_response.encode('utf-8'))
like image 3
Mihail Avatar answered Oct 24 '22 05:10

Mihail