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

2024-05-19 20:12:19 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在开发一个REST API,它接收来自一些真正的脑死亡软件的POST请求,这些软件无法修补或其他任何东西。文章将更新数据库中已经存在的模型对象。

具体来说,我发布的是具有相关字段(SlugRelatedField,海报知道“name”属性,但不知道“pk”)的对象的数据。但是,如果海报发送数据,而“name”在SlugRelatedField上不返回任何内容(例如,相关对象不存在),则需要返回404。我已经用一个调试器完成了这个过程,但是看起来DRF使用了一些Django信号魔法来完成它,就像DRF那样™,这将返回400个错误请求。我不知道如何修改这个-只有当它是上述条件,而不是一个真正的400有价值的职位-成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。


Tags: samplenameselfrestobjisresponserequest
2条回答

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

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

与其在API视图中使用pre_save为什么不重写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

Note that there is a difference between using get(), and using filter() with a slice of [0]. If there are no results that match the query, get() will raise a DoesNotExist exception. This exception is an attribute of the model class that the query is being performed on - so in the code above, if there is no Entry object with a primary key of 1, Django will raise Entry.DoesNotExist.

Similarly, Django will complain if more than one item matches the get() query. In this case, it will raise MultipleObjectsReturned, which again is an attribute of the model class itself.

相关问题 更多 >