Django REST框架:如果相关字段不存在,则在POST上返回404(不是400)? [英] Django REST Framework: return 404 (not 400) on POST if related field does not exist?

查看:815
本文介绍了Django REST框架:如果相关字段不存在,则在POST上返回404(不是400)?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发一个REST API,它从一些真正脑死亡的软件(不能PATCH或其他任何东西)接收POST请求。 POST是用于更新数据库中已存在的Model对象。

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.

具体来说,我为具有相关字段的对象(一个SlugRelatedField,POSTer知道'name'属性,而不是'pk')。但是,如果POSTer发送数据,那么在SlugRelatedField(相关对象不存在中,name不返回任何内容),我需要返回404。我已经通过一个调试器,但似乎DRF使用一些Django信号魔法来做它的方式DRF做It™,这是返回400 BAD请求。我不知道如何修改这个 - 只有,当它是上述条件,而不是一个真正的400值得的POST - 到404。

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.

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

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

这里是序列化程序:

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 没有在给定的测试中运行(但是在其他测试中运行):

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

如果我在$ code > self.rest_client.post()调用,响应在那时已经有一个400。

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

推荐答案

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

Instead of using pre_save why not override post in your API view:

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的状态:

Make sure you import DRF's status:

from rest_framework import status

此外,请注意,您可能希望更具体地了解您捕获的异常。 Django的 get 方法将返回 DoesNotExist ,如果没有匹配或 MultipleObjectsReturned 如果多个对象匹配。 相关文档

Also, note you will likely want to be more specific with the Exceptions you catch. Django's get method will return either DoesNotExist if nothing matches or MultipleObjectsReturned if more than one object matches. The relevant documentation:


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

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.

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

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.

这篇关于Django REST框架:如果相关字段不存在,则在POST上返回404(不是400)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆