Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

django: raise BadRequest as exception?

Is it possible to raise BadRequest as exception in django?

I have seen that you can raise a 404 [1].

Use case: in a helper method I load a json from request.GET. If the json was cut since the browser (IE) cut the url, I would like to raise a matching exception.

A BadRequest exception looks appropriate, but up to now there seems to no such exception in django.

In 1.6 there is a SuspiciousOperation exception. But this does not match in my case, since it is not security related.

Of course I could put a try..except around my helper method in the view method, but this is not DRY.

Has someone a solution where I don't need a try..exception around every call of my helper method?

[1] https://docs.djangoproject.com/en/1.6/ref/exceptions/#django.core.urlresolvers.Resolver404

Update

Code example:

def my_view(request):
    data=load_data_from_request(request) # I don't want a try..except here: DRY
    process_data(data)
    return django.http.HttpResponse('Thank you')

def load_data_from_request(request):
    try:
        data_raw=json.loads(...)
    except ValueError, exc:
        raise BadRequest(exc)
    ...
    return data
like image 582
guettli Avatar asked Aug 21 '14 08:08

guettli


People also ask

How do you increase validation error in DRF?

The easiest way to change the error style through all the view in your application is to always use serializer. is_valid(raise_exception=True) , and then implement a custom exception handler that defines how the error response is created.

How do I increase error in Django REST framework?

The generic views use the raise_exception=True flag, which means that you can override the style of validation error responses globally in your API. To do so, use a custom exception handler, as described above. By default this exception results in a response with the HTTP status code "400 Bad Request".

How do I return an error response in Django?

I want to return a HTTP 400 response from my django view function if the request GET data is invalid and cannot be parsed. Return a HttpResponseBadRequest : docs.djangoproject.com/en/dev/ref/request-response/… You can create an Exception subclass like Http404 to have your own Http400 exception.


3 Answers

The other answers are explaining how to return an HTTP response with 400 status.

If you want to hook into Django's 400 error handling, you can raise a SuspiciousOperation exception or a subclass of it.

See the docs here and here.

In your example it would look like:

from django.core.exceptions import SuspiciousOperation  def load_data_from_request(request):     try:         data_raw = json.loads(...)     except ValueError:         raise SuspiciousOperation('Invalid JSON')     # ...     return data 
like image 58
yprez Avatar answered Sep 29 '22 12:09

yprez


You need custom middleware to handle exception what you raise. Utilize custom exceptions to check for this condition in middleware.

class ErrorHandlingMiddleware(object):     def process_exception(self, request, exception):         if not isinstance(exception, errors.ApiException): # here you check if it yours exception             logger.error('Internal Server Error: %s', request.path,                 exc_info=traceback.format_exc(),                 extra={                     'request': request                 }             )         # if it yours exception, return response with error description         try:             return formatters.render_formatted_error(request, exception) # here you return response you need         except Exception, e:             return HttpResponseServerError("Error During Error Processing") 
like image 34
coldmind Avatar answered Sep 29 '22 12:09

coldmind


As an alternative to @coldmind's answer (converting exceptions in a middleware layer), you could put a decorator on your view function which does the same thing. Personally I prefer this, because it's just plain-old-Python, and doesn't require me dust off my knowledge of how Django middleware works.

You don't want to stream-of-conciousness inline all functionality in your view functions (this makes your view module depend on all your project's other modules, leading to 'everything depends on everything else' architecture) Instead, it's better if the view just knows about http. It extracts what you need from the request, delegates to some other 'business logic' function. The business logic might delegate to other modules (e.g. database code or interfaces to other external systems.) Then finally the return value from your business logic is converted into an http response by the view function.

But how to communicate errors back to the view function from the business logic (or whatever it delegates to)? Using return values is irksome for many reasons. For example, these error return values will have to be propogated back to the view from all through your whole codebase. This is often cripplingly messy because you will already be using the return values of functions for other purposes.

The natural way to deal with this is to use exceptions, but the Django view won't, by itself, convert uncaught exceptions into returned HTTP status codes (except for a couple of special cases, as the OP says.)

So. I write a decorator to apply to my view. The decorator converts various raised exception types into different returned django.http.HttpResponseXXX values. e.g:

# This might be raised by your business logic or database code, if they get
# called with parameters that turn out to be invalid. The database code needs
# know nothing about http to do this. It might be best to define these exception
# types in a module of their own to prevent cycles, because many modules 
# might need to import them.
class InvalidData(Exception):
    pass

# This decorator is defined in the view module, and it knows to convert
# InvalidData exceptions to http status 400. Add whatever other exception types
# and http return values you need. We end with a 'catch-all' conversion of
# Exception into http 500.
def exceptions_to_http_status(view_func):
    @wraps(view_func)
    def inner(*args, **kwargs):
        try:
            return view_func(*args, **kwargs)
        except InvalidData as e:
            return django.http.HttpResponseBadRequest(str(e))   
        except Exception as e:
            return django.http.HttpResponseServerError(str(e))
     return inner

 # Then finally we define our view, using the decorator.

 @exceptions_to_http_status
 def myview(self, request):
     # The view parses what we need out of incoming requests
     data = request.GET['somearg']

     # Here in the middle of your view, delegate to your business logic,
     # which can just raise exceptions if there is an error.
     result = myusecase(data)

     # and finally the view constructs responses
     return HttpResponse(result.summary)

Depending on circumstance, you might find the same decorator could work on many, or all, of your view functions.

like image 36
Jonathan Hartley Avatar answered Sep 29 '22 11:09

Jonathan Hartley