使用Django的测试客户端post方法传递参数并从rest_framework请求JSON来创建单元测试

时间:2016-07-22 17:32:04

标签: python json django unit-testing django-rest-framework

我想实例化django.test.client.Client()rest_framework.test.APIClient(),POST一组简单的参数,并从基于djangorestframework类的视图请求JSON格式响应。

文档suggests我只是实例化APIClient()并使用参数format='json'发布:

rest_framework.test import APIClient
apiclient = APIClient()
response = apiclient.post('/api/v1/model/1/run',
                          data=request_params, format='json')

然而,我的视图(DRF视图集自定义方法)不会收到请求参数。将此跟踪到视图,POST参数确实将其作为dict转到request.data,但request.POST.items()返回一个空列表。当我使用下面的代码从浏览器通过AJAX发出POST请求时,request.POST.items()会正确返回所有参数。只有在使用单元测试APIClient() post()方法时,参数值才会在request.POST.items()中。

如果我使用.get()的{​​{1}}方法,请求参数在到达视图时不在APIClient()中,但它们位于request.data中,传入request.GET.items()。 ClientHandler中的值从查询字符串移动到WSGIRequest GET QueryDict。调用在django.test.client第115行QUERY_STRING(Django 1.9.7)中。 request = WSGIRequest(environ) APIClient()似乎没有发生这种情况。

我尝试了以下内容:

  • post()传递给数据参数,但响应相同 - 我的视图未看到请求中的任何参数(ref)。

  • 使用Django客户端,传递json.dumps(request_params),有和没有json.dumps,但响应相同。

  • 使用Django Client,将post **额外参数设置为content_type='application/json'(有和没有json.dumps) - 相同的响应。

  • 使用HTTP_ACCEPT='application/json'(有和没有json.dumps)初始化Django客户端 - 相同的响应。

  • 保留HTTP_ACCEPT='application/json' HTTP标头,发布的content_type参数和APIClient的格式参数未定义,并将Accept添加到request_params - 对于{'format':'json'}请求,我的代码会看到请求参数,但rest_framework会返回HTML。在此HTML中呈现的JSON显示代码正常工作(返回状态202和轮询URL,应该如此)。

  • Client.get附加到单元测试中的网址,并将内容类型等保留为默认值,但我从get_response获取.json

我的代码可以通过浏览器接受AJAX POST请求,当我使用client.get()时,我的单元测试工作正常。它只是使用client.post()和需要JSON的组合,我无法工作。

我用以下内容提取请求值:

Not Found: /api/v1/model/1/run/.json

发送AJAX请求的Javascript,成功并返回JSON ,如下所示:

if request.method == 'POST':
    form_values = ((key, value) for key, value in request.POST.items())
else:
    form_values = ((key, value) for key, value in request.GET.items())

Accept标头是这个工作的原因,格式= json没有工作。

这是接收视图:

// Setup at the bottom of the HTML body
$(document).ready(function(){
    $.ajaxSetup({
      data: {csrfmiddlewaretoken: "{{ csrf_token }}", format: "json" }
    });
    $.ajaxSetup({
        beforeSend: function (xhr, settings) {
            xhr.setRequestHeader("Accept", "application/json");
            if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
                xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
            }
        }
    });
});

// Code that makes the request url=/api/v1/model/1/run, method=post 
// Only POST is permitted on the view method by decorator @detail_route(methods=['post']))
function run_model(event)
{
    var form = $(event.target);

    $.ajax({
        type: form.attr('method'),
        url: form.attr('action'),
        data: $("#" + form.attr('id')).serialize() + "&format=json&csrfmiddlewaretoken={{ csrf_token }}"
    })
    .done(function (data, status, jqXHR) {
        poll_instance(data.instance_id, data.model_id);
    })
    .fail(function (jqXHR, status, err) {
        var status_div = $("." + construct_div_class("model", "div", jqXHR.responseJSON.model_id)).children("div.status");
        if (catch_ajax_error(status_div, failed_tries, jqXHR, status, err)) {
            setTimeout(run_model, 3000, event);
        };
    });

    event.preventDefault();
};

表单,其提交与上面的run_model()相关联:

class ModelViewSet(viewsets.ModelViewSet):
    @detail_route(methods=['post'])
    def run(self, request, *args, **kwargs):
        """
        Runs a model and redirects to the URL that will return the output results when ready.
        """
        try:
            instance_id = run_model(request, self.get_object().id)

        except ParameterValidationError as e:

        # ...    

        return Response(data={'instance_id': instance_id, 'model_id': self.get_object().id},
                        status=status.HTTP_202_ACCEPTED)

我在Python 3.5,Django 1.9.7,djangorestframework 3.4.0(也发生在3.2.1),djangorestframework-xml 1.3.0,在PyCharm 2016.1中进行调试

1 个答案:

答案 0 :(得分:0)

证明AJAX数据假设出现在request.data中,我使用错误的方法从浏览器通过AJAX提交数据。 Django rest_framework(DRF)假设来自请求的数据将以与返回给客户端的数据相同的格式传递 - 在这种情况下是JSON两种方式。因为它假定对于Accept=application/json请求,传入的数据将采用JSON格式,它会自动解析它并为您填充request.datarequest.GETrequest.POST为空请求到达DRF视图的时间。

要在AJAX请求中传递数据形式,我使用jquery form plugin's .formSerialize()方法。

我只是从表单中编译了一个.map()字典,但这对于无线电和其他可能有多个值的单个键/表单ID的实例不起作用。

这个答案应该归功于@dhke,他指出了我的根本错误。虽然也许应该删除这个问题。