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)serializationof 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 = CharacterizationSerializerpermission_classes = (AnonPostAllowed,) # @todo XXX hack for braindead POSTerdef 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 hackelse: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.