Django REST框架:如果相关字段不存在,则在POST上返回404(而不是400)?

时间:2014-06-25 23:37:25

标签: python django django-rest-framework django-signals

我正在开发一个REST API,它接受来自一些真正无脑的软件的POST请求,这些软件可以补丁或其他任何东西。 POST用于更新数据库中已存在的Model对象。

具体来说,我为具有相关字段的对象发布数据(SlugRelatedField,因为POSTer知道' name'属性但不是' pk')。但是,如果POSTer将数据发送到' name&#39 ;,那么我需要返回404。在SlugRelatedField上没有返回任何内容(例如,相关对象不存在)。我已经通过调试器完成了这项工作,但似乎DRF使用了一些Django信号魔法来实现它DRF Does It™,这将返回400 BAD REQUEST。我不知道如何修改这个 - ,如果是上述条件,而不是真正的400值的POST - 进入404。

顺便说一句,我的视图中的pre_save()在执行失败测试期间没有执行。

这是序列化程序:

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',)

以下是pre_save未在给定测试中运行的视图(但在其他测试中运行):

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'])

好的方法是,这是失败的测试:

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))

如果我在self.rest_client.post()电话中加入了一个断点,那时响应已经有400个。

2 个答案:

答案 0 :(得分:6)

您可以使用Django快捷方式获取obj.sample:

from django.shortcuts import get_object_or_404
obj.sample = get_object_or_404(Sample, name=self.request.DATA['sample'])

答案 1 :(得分:5)

而不是使用pre_save为什么不在API视图中覆盖post

def post(self, request, *args, **kwargs):
    ...other stuff
    try:
        obj.sample = Sample.objects.get(name=self.request.DATA['sample'])
        ...or whatever other tests you want to do
    except:
        return Response(status=status.HTTP_404_NOT_FOUND)

    response = super(CharacterizationList, self).post(request, *args, **kwargs)
    return response

确保导入DRF的状态:

from rest_framework import status

另外,请注意,您可能希望更具体地了解您捕获的例外情况。如果没有匹配,Django的get方法将返回DoesNotExist或如果多个对象匹配则返回MultipleObjectsReturnedThe relevant documentation

  

请注意,使用get()和using之间存在差异   filter()的片[0]。如果没有与之匹配的结果   查询,get()将引发一个DoesNotExist异常。这是一个例外   正在执行查询的模型类的属性 - 所以   在上面的代码中,如果没有具有主键的Entry对象   1,Django将引发Entry.DoesNotExist。

     

同样,如果多个项目匹配,Django会抱怨   get()查询。在这种情况下,它会引发MultipleObjectsReturned,   这也是模型类本身的一个属性。