Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Tastypie-django custom error handling

I would like to return some JSON responses back instead of just returning a header with an error code. Is there a way in tastypie to handle errors like that?

like image 390
maksim-s Avatar asked Jan 10 '12 17:01

maksim-s


2 Answers

Figured it out eventually. Here's a good resource to look at, if anyone else needs it. http://gist.github.com/1116962

class YourResource(ModelResource):

    def wrap_view(self, view):
        """
        Wraps views to return custom error codes instead of generic 500's
        """
        @csrf_exempt
        def wrapper(request, *args, **kwargs):
            try:
                callback = getattr(self, view)
                response = callback(request, *args, **kwargs)

                if request.is_ajax():
                    patch_cache_control(response, no_cache=True)

                # response is a HttpResponse object, so follow Django's instructions
                # to change it to your needs before you return it.
                # https://docs.djangoproject.com/en/dev/ref/request-response/
                return response
            except (BadRequest, ApiFieldError), e:
                return HttpBadRequest({'code': 666, 'message':e.args[0]})
            except ValidationError, e:
                # Or do some JSON wrapping around the standard 500
                return HttpBadRequest({'code': 777, 'message':', '.join(e.messages)})
            except Exception, e:
                # Rather than re-raising, we're going to things similar to
                # what Django does. The difference is returning a serialized
                # error message.
                return self._handle_500(request, e)

        return wrapper
like image 73
maksim-s Avatar answered Nov 02 '22 15:11

maksim-s


You could overwrite tastypie's Resource method _handle_500(). The fact that it starts with an underscore indeed indicates that this is a "private" method and shouldn't be overwritten, but I find it a cleaner way than having to overwrite wrap_view() and replicate a lot of logic.

This is the way I use it:

from tastypie import http
from tastypie.resources import ModelResource
from tastypie.exceptions import TastypieError

class MyResource(ModelResource):

    class Meta:
        queryset = MyModel.objects.all()
        fields = ('my', 'fields')

    def _handle_500(self, request, exception):

        if isinstance(exception, TastypieError):

            data = {
                'error_message': getattr(
                    settings,
                    'TASTYPIE_CANNED_ERROR',
                    'Sorry, this request could not be processed.'
                ),
            }

            return self.error_response(
                request,
                data,
                response_class=http.HttpApplicationError
            )

        else:
            return super(MyResource, self)._handle_500(request, exception)

In this case I catch all Tastypie errors by checking if exception is an instance of TastypieError and return a JSON reponse with the message "Sorry, this request could not be processed.". If it's a different exception, I call the parent _handle_500 using super(), which will create a django error page in development mode or send_admins() in production mode.

If you want to have a specific JSON response for a specific exception, just do the isinstance() check on a specific exception. Here are all the Tastypie exceptions:

https://github.com/toastdriven/django-tastypie/blob/master/tastypie/exceptions.py

Actually I think there should be a better/cleaner way to do this in Tastypie, so I opened a ticket on their github.

like image 32
gitaarik Avatar answered Nov 02 '22 14:11

gitaarik