Reimus Klinsman
Reimus Klinsman

Reputation: 947

How do you return 404 when resource is not found in Django REST Framework

When a user inputs a url that is wrong, my Django app returns an HTML error. How can I get DRF to return a json formatted error?

Currently my urls is

from django.conf.urls import url
from snippets import views

urlpatterns = [
    url(r'^snippets/$', views.snippet_list),
    url(r'^snippets/(?P<pk>[0-9]+)/$', views.snippet_detail),
]

but if a user goes to 127.0.0.1:8000/snip They get the html formatted error rather than a json formatted error.

Upvotes: 46

Views: 79974

Answers (4)

Symon
Symon

Reputation: 671

Or simply, you can use the same structure of DRF, without losing I18N and keep the same DRF error message:

from rest_framework import viewsets, status, exceptions
from rest_framework.decorators import action
from rest_framework.response import Response

try:
  codename = get_or_bad_request(self.request.query_params, 'myparam')
  return Response(self.get_serializer(MyModel.objects.get(myparam=codename), many=False).data)
except MyModel.DoesNotExist as ex:
  exc = exceptions.NotFound()
  data = {'detail': exc.detail}
  return Response(data, exc.status_code)

Upvotes: 0

Masoud Gheisari
Masoud Gheisari

Reputation: 1497

An easier way is to use get_object_or_404 method in django:
as described in this link:

get_object_or_404(klass, *args, kwargs)
-
Calls get() on a given model manager, but it raises Http404 instead of the model’s DoesNotExist exception.
-
klass: A Model class, a Manager, or a QuerySet instance from which to get the object.

As an example, pay attention to

obj = get_object_or_404(Snippet, pk=pk)
return obj

in the following code:

from django.shortcuts import get_object_or_404
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
from rest_framework.views import APIView
from rest_framework.response import Response

class SnippetDetail(APIView):
    """
    Retrieve, update or delete a snippet instance.
    """
    def get_object(self, pk):
        obj = get_object_or_404(Snippet, pk=pk)
        return obj

    def get(self, request, pk, format=None):
        snippet = self.get_object(pk)
        serializer = SnippetSerializer(snippet)
        return Response(serializer.data)
    ...

Upvotes: 8

Anil Chauhan
Anil Chauhan

Reputation: 121

from rest_framework import status    
from rest_framework.response import Response

# return 404 status code    
return Response({'status': 'details'}, status=status.HTTP_404_NOT_FOUND)

Upvotes: 12

binpy
binpy

Reputation: 4194

Simply way to do it, you can use raise Http404, here is your views.py

from django.http import Http404

from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView

from yourapp.models import Snippet
from yourapp.serializer import SnippetSerializer


class SnippetDetailView(APIView):

    def get_object(self, pk):
        try:
            return Snippet.objects.get(pk=pk)
        except Snippet.DoesNotExist:
            raise Http404

    def get(self, request, pk, format=None):
        snippet = self.get_object(pk)
        serializer = SnippetSerializer(snippet)
        return Response(serializer.data, status=status.HTTP_200_OK)

You also can handle it with Response(status=status.HTTP_404_NOT_FOUND), this answer is how to do with it: https://stackoverflow.com/a/24420524/6396981

But previously, inside your serializer.py

from rest_framework import serializers

from yourapp.models import Snippet


class SnippetSerializer(serializers.ModelSerializer):
    user = serializers.CharField(
        source='user.pk',
        read_only=True
    )
    photo = serializers.ImageField(
        max_length=None,
        use_url=True
    )
    ....

    class Meta:
        model = Snippet
        fields = ('user', 'title', 'photo', 'description')

    def create(self, validated_data):
        return Snippet.objects.create(**validated_data)

To test it, an example using curl command;

$ curl -X GET http://localhost:8000/snippets/<pk>/

# example;

$ curl -X GET http://localhost:8000/snippets/99999/

Hope it can help..


Update

If you want to handle for all error 404 urls with DRF, DRF also provide about it with APIException, this answer may help you; https://stackoverflow.com/a/30628065/6396981

I'll give an example how do with it;

1. views.py

from rest_framework.exceptions import NotFound

def error404(request):
    raise NotFound(detail="Error 404, page not found", code=404)

2. urls.py

from django.conf.urls import (
  handler400, handler403, handler404, handler500)

from yourapp.views import error404

handler404 = error404

Makesure your DEBUG = False

Upvotes: 74

Related Questions