单元测试 Django JSON 视图
我正在为一些Django的json_view视图编写单元测试,但在将json字符串传递给视图时遇到了麻烦。我昨天发了一个相关的问题,关于如何从JS将json字符串传递给Django视图。问题在于,我在JS中只是直接传递了json字符串,而实际上我需要将这个字符串作为一个对象的属性来传递。因为没有这样做,导致字符串被当作查询字典的键来处理。现在我又遇到了类似的问题,不过这次是从Django的单元测试传递到Django视图。以下是我代码的简化版本,它产生了相同的结果。
class MyTestCase(TestCase):
def setUp(self):
self.u = User.objects.create_user('test','test','test')
self.u.is_active = True
self.u.save()
self.client.login(username='test',password='test')
def test_create_object_from_form(self):
"""Test the creation of the Instance from the form data."""
import json
json_string json.dumps({'resource':{'type':'book','author':'John Doe'}})
print(json_string)
response = self.client.post(reverse('ajax_view'),
{'form':json_string},'json')
self.assetNotContains(response,'error')
而视图的样子是这样的
@json_view
def ajax_view(request):
"""Process the incoming form data."""
if request.method == 'POST':
print(request.POST)
form_data = json.loads(request.POST['form'])
resource_data = form_data['resource']
form = MyUserForm(resource_data)
if form.is_valid():
...
这是当测试运行时两个打印语句的输出。json字符串是
{"resource": {"type": "book", "author": "John Doe"}}
而查询字典看起来是这样的
<QueryDict: {u'{\'form\': \'{"resource": {"type": "book", "author": "John Doe"}}\'}': [u'']}>
我对JS和ajax完全是个新手,所以不用担心伤到我的自尊心,答案可能就在我眼前,随时都能跳出来咬我。
2 个回答
感谢@Eric_Fortin让我注意到这个头部设置,但这并没有解决我在使用'client.post'时遇到的查询字典格式错误的问题。当我把请求方式从POST改成GET,并使用了XMLHttpRequest的头部设置后,我的查询字典就正常了。以下是目前的解决方案:
response = self.client.get(reverse('ajax_view'),
{'form':json_string},'json',
HTTP_X_REQUESTED_WITH='XMLHttpRequest')
这只是部分答案,因为这个请求是要修改服务器上的数据,应该使用POST而不是GET。
编辑:
这是我测试中最终的代码,它可以通过POST将一个JSON字符串传递到我的视图中:
response = self.client.post(reverse('ajax_view'),
{'form':json.dumps(json_dict)})
现在从视图中打印出来显示查询字典格式正确。
<QueryDict: {u'form': [u'{"resource": {"status": "reviewed", "name": "Resource Test", "description": "Unit Test"}}']}>
我是在和我的一个同事一起调试时找到这个答案的,去掉了content_type 'json'后,查询字典的格式问题就解决了。被测试的视图并没有使用或调用'HttpRequest.is_ajax()',发送XMLHttpRequest的头部对我的问题没有影响,虽然加上这个头部是个好习惯,因为这个请求是一个ajax请求。
最后编辑
我最开始说在发帖时需要加上头信息 HTTP_X_REQUESTED_WITH='XMLHttpRequest'
,但现在在测试中发现这个说法不对。这个头信息对csrf中间件是必要的,但在测试中csrf是关闭的。不过,我还是觉得在测试中加上这个头信息是个好习惯,因为大多数JavaScript库在进行ajax请求时默认都会加上这个头信息。而且,如果有其他代码没有关闭csrf而使用了is_ajax方法,你就不用花几个小时去调试单元测试,去找头信息缺失的问题。
问题出在内容类型上,因为当Django接收到的内容类型不是 text/html
时,它就不会使用默认的post数据处理方式,而是会把你的数据格式化成查询字符串的样子,比如 type=book&author=JohnDoe
。
所以修正后的代码是:
response = self.client.post(reverse('ajax_view'),
{'form':json_string},
HTTP_X_REQUESTED_WITH='XMLHttpRequest')
这是我自己使用的方式:
post_data = {
"jsonrpc" : "2.0", "method": method, "params" : params, "id" : id }
return client.post('/api/json/',
json.dumps(post_data), "text/json",
HTTP_X_REQUESTED_WITH='XMLHttpRequest')
用来做一些json-rpc。注意,由于我传递的内容类型与默认值不同,我的数据在post请求中是原样传递的。