django: ¿por qué el objeto request.POST es inmutable?

109

Como pregunta el título, ¿por qué los chicos de Django decidieron implementar el objeto request.POST con un querydict (que, por supuesto, a su vez, hace que todo sea inmutable?)

Sé que puedes mutilarlo haciendo una copia de los datos de la publicación.

post = request.POST.copy()

pero ¿por qué hacer esto? Seguramente sería más simple permitir que la cosa fuera mutable de todos modos. ¿O también se está utilizando por alguna otra razón que podría causar problemas?

bharal
fuente
1
¿Por qué quieres que sea mutable? Puede tomar los datos de él y usarlo / modificarlo en su vista. Al agregarle datos, podría crear la impresión de que request.POSTse ha enviado con más datos de los que realmente ha sido.
Simeon Visser
11
No es que quiera que sea mutable. No más de, digamos, me gustaría que el helado estuviera frío. Sin embargo, en el caso del helado, si no está frío, se derrite y luego te regañan por hacer un gran desastre. Pero con el objeto request.POST ... quiero decir, si voy a estropear mi código, lo voy a estropear. No sabía que había una variedad endémica de desarrolladores que agregaban datos a los objetos POST y causaban problemas, por lo que parece algo extraño para "arreglar".
bharal
Buena pregunta; Realmente nunca pensé en eso.
Burhan Khalid
1
Esto me surgió esporádicamente porque mi cliente a veces enviaba datos JSON (mutables) y, a veces, mensajes codificados en forma de URL (inmutables).
owenfi
2
Para quienes no hablan inglés, "mutificar" no es una palabra; la frase correcta es "puedes mutarla" o "puedes modificarla". Tampoco hay necesidad de clasificar a los desarrolladores: puede usar "equipo de Django" o "desarrolladores centrales" en lugar de "chicos".
alexmuller

Respuestas:

131

Es un poco misterioso, ¿no? Varias teorías superficialmente plausibles resultan estar equivocadas en la investigación:

  1. ¿Para que el POSTobjeto no tenga que implementar métodos de mutación? No: la POSTpropiedad pertenece a la django.http.QueryDictclase , que implementa un conjunto completo de procedimientos de mutación incluyendo __setitem__, __delitem__, popy clear. Implementa la inmutabilidad marcando una bandera cuando llamas a uno de los métodos de mutación. Y cuando llamas al copymétodo, obtienes otra QueryDictinstancia con la bandera mutable activada.

  2. ¿Para mejorar el rendimiento? No: la QueryDictclase no obtiene ningún beneficio de rendimiento cuando la bandera mutable está desactivada.

  3. ¿Para que el POSTobjeto se pueda utilizar como clave de diccionario? No: los QueryDictobjetos no se pueden mezclar con hash.

  4. ¿Para que los POSTdatos se puedan construir de forma perezosa (sin comprometerse a leer la respuesta completa), como se afirma aquí ? No veo evidencia de esto en el código: por lo que puedo decir, la totalidad de la respuesta siempre se lee, ya sea directamente o a través MultiPartParserde las multipartrespuestas.

  5. ¿Para protegerte contra errores de programación? He visto este reclamo, pero nunca he visto una buena explicación de qué son estos errores y cómo la inmutabilidad te protege contra ellos.

En cualquier caso, POSTes no siempre inmutable : cuando la respuesta es multipart, entonces POSTes mutable. Esto parece poner freno a la mayoría de las teorías que se te ocurran. (A menos que este comportamiento sea un descuido).

En resumen, no veo una justificación clara en Django para que el POSTobjeto sea inmutable para las no multipartsolicitudes.

Gareth Rees
fuente
He notado toneladas de asperezas como esta en Django. Sin embargo, debe haber tenido sentido para alguien en algún momento.
Dan Passaro
2
Encontré esto en otra respuesta de Stack: "Y debe ser inmutable para que se pueda compilar de manera perezosa. La copia obliga a obtener todos los datos POST. Hasta la copia, es posible que no se recuperen todos. Además, para un WSGI de subprocesos múltiples servidor funciona razonablemente bien, es útil si esto es inmutable "
Seaux
12
@Seaux no debería leer SO respuestas perezosamente cuando tenga la intención de comentarlas. ;-)
Chris Wesseling
3
@ChrisWesseling Veo lo que hiciste allí
Seaux
2
Aún mejor, el querydict es mutable cuando envío la solicitud demandando al cliente de prueba de django.
user1158559
82

Si la solicitud fue el resultado de un formenvío de Django , entonces es razonable que POST immutablegarantice la integridad de los datos entre el envío del formulario y la validación del formulario . Sin embargo, si la solicitud no se envió a través de un formenvío de Django , entonces POST es mutableporque no hay validación del formulario.

Siempre puedes hacer algo como esto: (según el comentario de @ leo-the-manic )

#  .....
mutable = request.POST._mutable
request.POST._mutable = True
request.POST['some_data'] = 'test data'
request.POST._mutable = mutable
# ......
un33k
fuente
3
@JoshK: Supongo que el comentarista quería hacer que POST fuera mutable, y el fragmento de código en esta respuesta ayudó.
ShreevatsaR
Puede agregar una nueva clave, valor pero no puede cambiar los datos existentes.
Vamsidhar Muggulla
Agradable. Y estoy seguro de que quien use este código sabe lo que está haciendo.
John Pang
@VamsidharMuggulla Es posible agregar y cambiar. Incluso se permite borrar.
Antony Hatchkins
5

Actualización :

Gareth Rees tenía razón en que los puntos 1 y 3 no eran válidos en este caso. Aunque creo que los puntos 2 y 4 siguen siendo válidos, dejaré las tesis aquí.

(Me di cuenta de que el request.POSTobjeto tanto de Pyramid (Pylon) como de Django es una forma de MultiDict. Así que quizás sea una práctica más común que hacer request.POSTinmutable).


No puedo hablar por los chicos de Django, aunque me parece que podría hacerlo por algunas de estas razones:

  1. Rendimiento . Los objetos inmutables son "más rápidos" que los mutables en el sentido de que permiten optimizaciones sustanciales. Un objeto es inmutable significa que podemos asignarle espacio en el momento de la creación y los requisitos de espacio no cambian. También tiene cosas como eficiencia de copia y eficiencia de comparación debido a eso. Editar : este no es el casoQueryDictcomo señaló Gareth Rees.
  2. En el caso de request.POST, parece que ninguna actividad en el lado del servidor debería necesitar alterar los datos de la solicitud . Y, por lo tanto, los objetos inmutables son más adecuados, sin mencionar que tienen una ventaja de rendimiento sustancial.
  3. Los objetos inmutables se pueden usar como dictclaves, lo que supongo que podría ser muy útil en algún lugar de Django .. Editar : mi error, inmutable no implica directamente hash ; Sin embargo, los objetos hash , normalmente también son inmutables .
  4. Cuando pase request.POST(especialmente a complementos de terceros y hacia fuera), puede esperar que este objeto de solicitud del usuario permanezca sin cambios.

De alguna manera, estas razones también son respuestas genéricas a "inmutable vs mutable?" pregunta. Estoy seguro de que hay muchas más consideraciones de diseño que las anteriores en el caso de Django.

KZ
fuente
1
El último caso es realmente importante. Realmente se trata de seguridad. Esta es la razón por la que Django proporciona una sessionsforma breve de obtener y modificar datos entre estados.
CppLearner
2
Su punto (1) no puede ser la respuesta en este caso, porque POSTes un QueryDictobjeto , y estos objetos no obtienen ningún beneficio de rendimiento por ser inmutables. Y su punto (3) no puede ser la respuesta, porque los QueryDictobjetos no se pueden usar con hash y, por lo tanto, no se pueden usar como claves de diccionario.
Gareth Rees
@GarethRees Gracias por señalarlos. De hecho, estaba equivocado. Actualicé mi respuesta para corregir estos. Debería haberle prestado más atención QueryDictantes de responder.
KZ
7
@CppLearner El punto de seguridad parece discutible, por ejemplorequests.POST._mutable = True; requests.POST['foo'] = 'bar'; request.POST._mutable = False
Dan Passaro
4

Me gusta que sea inmutable por defecto. Como se señaló, puede hacerlo mutable si es necesario, pero debe ser explícito al respecto. Es como 'Sé que puedo hacer que la depuración de mi formulario sea una pesadilla, pero sé lo que estoy haciendo ahora'.

pawelmech
fuente
2

Encontré esto en un comentario en Stack Answer https://stackoverflow.com/a/2339963

Y debe ser inmutable para que se pueda construir con pereza. La copia obliga a obtener todos los datos POST. Hasta la copia, es posible que no se recupere todo. Además, para que un servidor WSGI de subprocesos múltiples funcione razonablemente bien, es útil si esto es inmutable

Seaux
fuente