Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django REST Framework: return 404 (not 400) on POST if related field does not exist?

I'm developing a REST API which takes POST requests from some really brain-dead software which can't PATCH or anything else. The POSTs are to update Model objects which already exist in the database.

Specifically, I'm POSTing data for objects with a related field (a SlugRelatedField, as the POSTer knows the 'name' attribute but NOT the 'pk'). However, I need to return a 404 if the POSTer sends data where the 'name' returns nothing on the SlugRelatedField (e.g. the related object does not exist). I've been through this with a debugger but it seems that DRF uses some Django signals magic to do it The Way DRF Does It™, which is to return a 400 BAD REQUEST. I don't know how to modify this - only when it's the above condition and not a true 400-worthy POST - into a 404.

By the way, pre_save() in my view is NOT executing during execution of the failing test.

Here's the serializer:

class CharacterizationSerializer(serializers.ModelSerializer):
    """
    Work-in-progress for django-rest-framework use.  This handles (de)serialization
    of data into a Characterization object and vice versa.

    See: http://www.django-rest-framework.org/tutorial/1-serialization
    """
    creator = serializers.Field(source='owner.user.username')
    sample = serializers.SlugRelatedField(slug_field='name',
                                          required=True,
                                          many=False,
                                          read_only=False)

    class Meta:
        model = Characterization
        # leaving 'request' out because it's been decided to deprecate it. (...maybe?)
        fields = ('sample', 'date', 'creator', 'comments', 'star_volume', 'solvent_volume',
                  'solution_center', 'solution_var', 'solution_minimum', 'solution_min_stddev',
                  'solution_test_len',)

And here's the view where pre_save isn't being run in the given test (but does get run in some others):

class CharacterizationList(generics.ListCreateAPIView):
    queryset = Characterization.objects.all()
    serializer_class = CharacterizationSerializer
    permission_classes = (AnonPostAllowed,)   # @todo XXX hack for braindead POSTer

    def pre_save(self, obj):
        # user isn't sent as part of the serialized representation,
        # but is instead a property of the incoming request.
        if not self.request.user.is_authenticated():
            obj.owner = get_dummy_proxyuser()   # this is done for CharacterizationList so unauthed users can POST. @todo XXX hack
        else:
            obj.owner = ProxyUser.objects.get(pk=self.request.user.pk)

        # here, we're fed a string sample name, but we need to look up
        # the actual sample model.
        # @TODO: Are we failing properly if it doesn't exist?  Should
        # throw 404, not 400 or 5xx.
        # except, this code doesn't seem to be run directly when debugging.
        # a 400 is thrown; DRF must be bombing out before pre_save?
        obj.sample = Sample.objects.get(name=self.request.DATA['sample'])

And for good measure, here's the failing test:

def test_bad_post_single_missing_sample(self):
    url = reverse(self._POST_ONE_VIEW_NAME)

    my_sample_postdict = self.dummy_plqy_postdict.copy()
    my_sample_postdict["sample"] = "I_DONT_EXIST_LUL"
    response = self.rest_client.post(url, my_sample_postdict)
    self.assertTrue(response.status_code == 404,
                    "Expected 404 status code, got %d (%s). Content: %s" % (response.status_code, response.reason_phrase, response.content))

If I put a breakpoint in at the self.rest_client.post() call, the response already has a 400 in it at that point.

like image 332
Gordon Morehouse Avatar asked Jun 25 '14 23:06

Gordon Morehouse


People also ask

What is 404 Not Found error in Django?

Raised when a resource does not exists at the given URL. This exception is equivalent to the standard Http404 Django exception. By default this exception results in a response with the HTTP status code "404 Not Found". Raised when an incoming request occurs that does not map to a handler method on the view.

What is a 400 Bad Request error in Django?

By default this exception results in a response with the HTTP status code "400 Bad Request". Django REST Framework provides two error views suitable for providing generic JSON 500 Server Error and 400 Bad Request responses. (Django's default error views provide HTML responses, which may not be appropriate for an API-only application.)

What is Django REST framework?

Django Rest Framework – An Introduction. Let’s look at how to create a RESTFul API for our Django Talk Project using Django Rest Framework (DRF), which is an application used for rapidly building RESTful APIs based on Django models.

How to raise http404 exception in Django?

Http404 uses a default 404 page which Django provides. To use it just raise Http404 exception in your code like this: You can also provide an error message while raising Http404 exception. Open views.py file and amend post_detail () view as follows:


1 Answers

You can use a Django Shortcut for that, getting the obj.sample:

from django.shortcuts import get_object_or_404
obj.sample = get_object_or_404(Sample, name=self.request.DATA['sample'])
like image 149
fixmycode Avatar answered Sep 24 '22 00:09

fixmycode