... | ... | @@ -6,12 +6,69 @@ Before continuing, please read the [Exceptions](https://www.django-rest-framewor |
|
|
|
|
|
The only exceptions handled by DRF's exception handler are subclasses of `rest_framework.exceptions.APIException`, which means that when explicitly raising an exception in a serializer or view, it MUST be a subclass of `APIException`. If any other type of exception is raised it will not be handled by the DRF exception handler, and will instead result in a Django error page (when DEBUG is true).
|
|
|
|
|
|
These are the subclasses of `APIException` provided by Django REST Framework. You can also create your own subclasses of `APIException` if needed. The implementation for these exceptions can be [found here](https://github.com/encode/django-rest-framework/blob/master/rest_framework/exceptions.py)
|
|
|
These are the subclasses of `APIException` provided by Django REST Framework. You can also create your own subclasses of `APIException` if needed. The implementation for these exceptions can be [found here](https://github.com/encode/django-rest-framework/blob/master/rest_framework/exceptions.py).
|
|
|
|
|
|
`APIException`, `ValidationError`, `ParseError`, `AuthenticationFailed`, `NotAuthenticated`, `PermissionDenied`, `NotFound`, `MethodNotAllowed`, `NotAcceptable`, `UnsupportedMediaType`, `Throttled`
|
|
|
|
|
|
### Special case of PermissionDenied and Http404
|
|
|
### Non DRF exceptions
|
|
|
|
|
|
Since DRF can only handle the above exceptions, we must catch and convert any other type of exception that could be raised. Here's a common example:
|
|
|
|
|
|
```python
|
|
|
user = EmailUser.objects.get(pk=1234)
|
|
|
```
|
|
|
|
|
|
If a `EmailUser` with the id `1234` does not exist an exception that is a subclass of `django.core.exceptions.ObjectDoesNotExist` will be raised. As the exception is not a subclass of `rest_framework.exceptions.APIException`, the error will not be handled by DRF's exception handler and we'll instead see something like this.
|
|
|
|
|
|

|
|
|
|
|
|
To properly handle this error, we must explicitly catch it and return a corresponding error that DRF can handle.
|
|
|
|
|
|
```python
|
|
|
from rest_framework import exceptions
|
|
|
|
|
|
try:
|
|
|
user = EmailUser.objects.get(pk=1234)
|
|
|
except EmailUser.DoesNotExist:
|
|
|
raise exceptions.NotFound("User not found")
|
|
|
```
|
|
|
|
|
|
Now we'll get a proper error message that the frontend can use!
|
|
|
|
|
|

|
|
|
|
|
|
### Special case of PermissionDenied and Http404
|
|
|
|
|
|
The *only* exceptions to this rule are `django.core.exceptions.PermissionDenied` and `django.http.Http404`, which DRF's exception handler specifically converts to `rest_framework.exceptions.PermissionDenied` and `rest_framework.exceptions.NotFound` ([implementation](https://github.com/encode/django-rest-framework/blob/master/rest_framework/views.py#L82))
|
|
|
|
|
|
### Similarly named exceptions
|
|
|
|
|
|
There are some DRF exception names that overlap exceptions from Django or Python builtin's. For example `ValidationError`, `PermissionDenied` etc. Make sure you know which type of exception you're raising and be sure that it's the one from DRF! What can help is if you need to use Django's `ValidationError` or `PermissionDenied` error, import them with an alias so that you can handle them and conver them to the right DRF type:
|
|
|
|
|
|
```python
|
|
|
from django.core.exceptions import ValidationError as DjangoValidationError
|
|
|
from rest_framework import exceptions
|
|
|
|
|
|
try:
|
|
|
django_validate_password(password)
|
|
|
except DjangoValidationError as error:
|
|
|
raise exceptions.ValidationError(error.messages)
|
|
|
```
|
|
|
|
|
|
## Always raise an exception, never manually return a `Response` with an error status code
|
|
|
|
|
|
It may be tempting sometimes to return a `rest_framework.respose.Response` instead of raising an exception:
|
|
|
|
|
|
```python
|
|
|
return Response("Something went wrong", status=status.HTTP_400_BAD_REQUEST)
|
|
|
```
|
|
|
|
|
|
This will appear to work fine, and the frontend *may* even be able to handle it properly, but only if the response body matches a format that the frontend expects when it encounters `APIException` errors. In any case, it's always better to raise an exception!
|
|
|
|
|
|
## `ValidationError` must only be raised in the `validate` or `validate_<field>` methods of a serializer
|
|
|
## Messages will be displayed to the user! |
|
|
\ No newline at end of file |
|
|
|
|
|
This one definitely took me the most time to realize, and it isn't necessarily a rule, but it makes a huge difference when handling errors from a frontend.
|
|
|
|
|
|
DRF's `ValidationError` is handled differently than the rest of the `APIException` subclasses. Rather than having a `detail: str` message body, it'll return a dict of errors related to either specific fields, or `non_field_errors` if not related to any specific field.
|
|
|
|
|
|
## Assume messages will be displayed to the user! |
|
|
\ No newline at end of file |