Django的Querydict奇怪的行为:将POST字典串成一个单独的键

时间:2011-06-11 12:36:22

标签: python django rest post

在django中使用测试客户端时,我遇到了一种非常奇怪的行为。

我正在使用POST将数据发送到我的django应用。我通常是从iPhone应用程序和/或测试html表单执行此操作。在服务器端,这就是我处理它的方式:

def handle_query(request):
   print request
   q = con.QueryLog()
   q.ID = request.POST.get('ID', '')
   q.device = request.POST.get('device-model', '')
   ....

这个print语句看起来像你期望的那样,即post请求中的每个参数都变成了字典中的一个键:

POST:QueryDict:{u'app-version':[u'3.0'],u'server-version':[u'v3d0'],

然而,我开始使用Django的测试客户端编写一些测试,无论我尝试什么,我在post请求中发送的POST参数字典都会聚集到QueryDict中的单个密钥中。请允许我用一些代码来说明:

类SearchTest(TestCase):     def setUp(self):         通

def test_search(self):
    request = HttpRequest()
    data = '{"amzn_locale": "com"}'
    # request._raw_post_data = data
    resp = self.client.post(
        '/is/', 
        data=data,
        content_type='application/x-www-form-urlencoded',
        # content_type='application/json',
        )

服务器端的相同print语句显示字典无法解释为字符串:

POST: QueryDict: {u'{"amzn_locale":"com"}': [u'']}>,

如果我将数据设置为实际字典,同样的事情

data = {"amzn_locale": "com"}

设置request._raw_post_data不会改变任何内容。也没有改变

content_type='application/json'

非常感谢任何帮助。从这个stackoverflow问题看来,我似乎不是第一个碰到这个问题 iphone Json POST request to Django server creates QueryDict within QueryDict

3 个答案:

答案 0 :(得分:7)

问题是你提供的是content_type。由于您这样做,客户端期待像

这样的urlencoded字符串
"username=hi&password=there&this_is_the_login_form=1"

而不是像

这样的字典
{'username': 'hi', 'password': 'there', 'this_is_the_login_form': 1}

如果删除content_type kwarg,你会没事的。

编辑:事实证明,如果传入除MULTIPART_CONTENT之外的任何content_type,测试客户端将查找url编码的字符串 - content_type将仅用于确定要用于编码的字符集那个url编码的字符串。记录在案here。相关位读取:

  

如果您提供content_type(例如,XML有效负载的text / xml),则数据内容将使用HTTP Content-Type标头中的content_type在POST请求中按原样发送。

     

如果您没有为content_type提供值,则数据中的值将以内容类型multipart / form-data传输。在这种情况下,数据中的键值对将被编码为多部分消息,并用于创建POST数据有效负载。

答案 1 :(得分:1)

编辑:当然,正好在我开始看的那一行上面就是正确的答案。 post_data基于content_type以不同方式处理。请参阅以下答案。无需应用以下更改。

所以看起来正在发生的事情是你传入post的数据被字符串编码函数立即变为dict的字符串表示,后面的QueryDict无法读取。我不知道预期的行为是什么,但是如果你在序列化之前对帖子数据进行了urlencode,那么它至少应该在QueryDict中以所需的形式到达。在django / test / client.py中我们看到第244行

post_data = smart_str(data, encoding=charset)

它只是使dict变平并将其序列化。可能的解决方法是在序列化之前应用与GET使用相同的格式,因此

post_data = smart_str(urlencode(data, doseq=True), encoding=charset)

这对我来说似乎是合理的,但我不能保证它在其他地方没有后果。看起来您可以在调用client.post之前在代码中执行上述转换,但我还没有测试过。

答案 2 :(得分:0)

但是您将其声明为字符串 - 您在data的值周围有单引号。