Raising exceptions with django_rest_framework
Question: I have written a view that decrypts a GPG encrypted file and returns it as
plain text. This works fine in general. The problem is, if the file is empty
or otherwise contains invalid GPG data, gnupg returns an empty result rather
than throw an exception.

I need to be able to do something like this inside _decrypt_file_ to check to
see if the decryption failed and raise an error:

    
    
    if data.ok:
        return str(data)
    else:
        raise APIException(data.status)
    

If I do this, I see the APIException raised in the Django debug output, but
it's not translating to a 500 response to the client. Instead the client gets
a 200 response with an empty body. I can raise the APIException in my _get_
method and it sends a 500 response, but there I don't have access to the gnupg
error message.

Here is a very simplified version of my view:

    
    
    from rest_framework.views import APIView
    from django.http import FileResponse
    from django.core import files
    from gnupg import GPG
    
    class FileDownload(APIView):
        def decrypt_file(self, file):
            gpg = GPG()
            data = gpg.decrypt(file.read())
            return data
    
        def get(self, request, id, format=None):
            f = open('/tmp/foo', 'rb')
            file = files.File(f)
            return FileResponse(self.decrypt_file(file))
    

I have read the docs on DRF exception handling [here](http://www.django-rest-
framework.org/api-guide/exceptions/), but it doesn't seem to provide a
solution to this problem. I am fairly new to Django and python in general, so
it's entirely possible I'm missing something obvious. Helpful advice would be
appreciated. Thanks.


Answer: If you raise an error in python, every function in the trace re-raise it until
someone catch it. You can first declare a exception :

    
    
    from rest_framework.exceptions import APIException
    
    class ServiceUnavailable(APIException):
        status_code = 503
        default_detail = 'Service temporarily unavailable, try again later.'
    

Then in your `decrypt_file` function, raise this exception if decryption is
not successful. You can pass an argument to modify the message. Then, in the
`get` method, you should call `decrypt_file` function and pass your file as an
argument. If any things goes wrong, your function raise that exception and
then, get method re-raise it until Django Rest Framework exception handler
catch it.  
**EDIT:**  
In your decrypt function, do something like this:

    
    
    from rest_framework.response import Response
    
    def decrypt_file(self, file):
        ... # your codes
        if data.ok:
            return str(data)
        else:
            raise ServiceUnavailable
    
    def get(self, request, id, format=None):
        f = open('/tmp/foo', 'rb')
        result = decrypt_file(f)
        f.close()
        return Response({'data': result})
    

