El extraño comportamiento de Django en Querydict: agrupa el diccionario POST en una sola clave

Estoy experimentando un comportamiento realmente extraño cuando uso el cliente de prueba en django.

Estoy usando un POST para enviar datos a mi aplicación django. Generalmente hago esto desde una aplicación de iPhone y / o un formulario html de prueba. En el lado del servidor, así es como lo manejo:

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

Esa statement de impresión se parece a lo que usted esperaría, es decir, cada parámetro en la solicitud posterior se convierte en una clave en el diccionario:

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

Sin embargo, comencé a escribir algunas pruebas utilizando el cliente de prueba de Django, y no importa lo que intente, el diccionario de parámetros POST que envío en la solicitud posterior se agrupa en una sola clave en el QueryDict . Permítanme ilustrar con algún código:

clase SearchTest (TestCase): def setUp (self): pass

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

La misma statement de impresión en el lado del servidor muestra la inexplicable agrupación del diccionario en una cadena:

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

Si configuro los datos en un diccionario real, lo mismo

 data = {"amzn_locale": "com"} 

Al establecer la solicitud, no se modifica nada. Tampoco cambia

 content_type='application/json' 

Cualquier ayuda sería muy apreciada. A partir de esta pregunta de stackoverflow, parece que no soy el primero en ejecutarme en esta solicitud POST de Json del iPhone para que el servidor Django cree QueryDict dentro de QueryDict

El problema es que estás suministrando un tipo de contenido. Como lo hizo, el cliente espera una cadena urlencoded como

 "username=hi&password=there&this_is_the_login_form=1" 

en lugar de un diccionario como

 {'username': 'hi', 'password': 'there', 'this_is_the_login_form': 1} 

Si eliminas el content_type kwarg estarás bien.

Edición: Como resultado, el cliente de prueba buscará una cadena codificada en url si pasas cualquier tipo de contenido diferente a MULTIPART_CONTENT – el tipo de contenido solo se usará para averiguar qué conjunto de caracteres usar para codificar esa cadena codificada en url. Esto está documentado aquí . El bit relevante lee:

Si proporciona content_type (por ejemplo, text / xml para una carga útil XML), el contenido de los datos se enviará tal como está en la solicitud POST, usando content_type en el encabezado HTTP Content-Type.

Si no proporciona un valor para content_type, los valores en los datos se transmitirán con un tipo de contenido de multipart / form-data. En este caso, los pares clave-valor en los datos se codificarán como un mensaje de varias partes y se utilizarán para crear la carga útil de datos POST.

Edit: Y, por supuesto, justo en la línea sobre la que empecé a mirar es la respuesta correcta. post_data se maneja de forma diferente según el tipo de contenido. Vea la respuesta a continuación. No es necesario aplicar el cambio a continuación.

Por lo tanto, parece que lo que está sucediendo es que el dictado de datos que se pasa a publicar se está aplanando de inmediato por una función de encoding de cadena en una representación de cadena del dict, que el QueryDict más adelante en la línea no puede leer. No sé cuál es el comportamiento deseado, pero si usted codifica los datos de la publicación antes de que se serialicen, al menos debería llegar al formulario requerido en el QueryDict. En django / test / client.py vemos la línea 244

 post_data = smart_str(data, encoding=charset) 

lo que simplemente aplana el dictado y lo serializa. Una posible solución sería aplicar el mismo formato que utiliza GET antes de la serialización, por lo tanto

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

Esto me parece razonable, aunque no puedo garantizar que no tenga consecuencias en otros lugares. Parece que podrías hacer la transformación anterior en tu código antes de la llamada a client.post, pero no lo he probado.

Pero lo declaró como una cadena: tiene comillas simples alrededor del valor de los data .