django - pourquoi l'objet request.POST est-il immuable?

109

Comme le titre le demande, pourquoi les gars de Django ont-ils décidé d'implémenter l'objet request.POST avec un querydict (qui, bien sûr, à son tour, rend le tout immuable?)

Je sais que vous pouvez le modifier en faisant une copie des données de publication

post = request.POST.copy()

mais pourquoi faire ça? Ce serait sûrement plus simple de permettre à la chose d'être mutable de toute façon? Ou est-il également utilisé pour une autre raison qui pourrait causer des problèmes?

bharal
la source
1
Pourquoi voulez-vous qu'il soit modifiable? Vous pouvez en extraire les données et les utiliser / les modifier dans votre vue. En y ajoutant des données, vous pouvez créer l'impression qui request.POSTa été soumise avec plus de données qu'elle ne l'a été en réalité.
Simeon Visser le
11
Ce n'est pas que je veuille que ce soit mutable. Pas plus que, disons, je voudrais que la crème glacée soit froide. Dans le cas de la crème glacée, si elle n'est pas froide, elle fond et vous vous faites gronder pour avoir fait un gros gâchis. Mais avec l'objet request.POST ... Je veux dire, si je vais bousiller mon code, je vais le foutre en l'air. Je n'étais pas au courant qu'il y avait une endémie de développeurs ajoutant des données aux objets POST et causant des problèmes, donc cela semble être une chose étrange à cibler pour "corriger".
bharal du
Bonne question; jamais pensé à cela vraiment.
Burhan Khalid
1
Cela s'est produit sporadiquement pour moi parce que mon client soumettait parfois des données JSON (mutables) et parfois des messages codés au format URL (immuables).
owenfi
2
Pour les non-anglophones, "mutify" n'est pas un mot - la phrase correcte est "vous pouvez le muter" ou "vous pouvez le modifier". Il n'y a pas non plus besoin de sexe des développeurs - vous pouvez utiliser "Django team" ou "core devs" plutôt que "guys".
alexmuller

Réponses:

131

C'est un peu un mystère, n'est-ce pas? Plusieurs théories superficiellement plausibles se révèlent erronées lors de l'enquête:

  1. Pour que l' POSTobjet n'ait pas à implémenter de méthodes de mutation? Non: l' POSTobjet appartient à la django.http.QueryDictclasse , qui met en œuvre un ensemble complet de méthodes de mutation , y compris __setitem__, __delitem__, popet clear. Il implémente l'immuabilité en vérifiant un indicateur lorsque vous appelez l'une des méthodes de mutation. Et lorsque vous appelez la copyméthode, vous obtenez une autre QueryDictinstance avec l'indicateur mutable activé.

  2. Pour une amélioration des performances? Non: la QueryDictclasse ne bénéficie d'aucun avantage en termes de performances lorsque l'indicateur mutable est désactivé.

  3. Pour que l' POSTobjet puisse être utilisé comme clé de dictionnaire? Non: les QueryDictobjets ne sont pas hachables.

  4. Pour que les POSTdonnées puissent être construites paresseusement (sans s'engager à lire toute la réponse), comme revendiqué ici ? Je ne vois aucune preuve de cela dans le code: pour autant que je sache, l'ensemble de la réponse est toujours lu, soit directement , soit via MultiPartParserpour les multipartréponses.

  5. Pour vous protéger contre les erreurs de programmation? J'ai vu cela revendiqué, mais je n'ai jamais vu une bonne explication de ce que sont ces erreurs et comment l'immuabilité vous protège contre elles.

Dans tous les cas, POSTn'est pas toujours immuable : lorsque la réponse est multipart, alors POSTest mutable. Cela semble mettre le kibosh sur la plupart des théories auxquelles vous pourriez penser. (À moins que ce comportement ne soit un oubli.)

En résumé, je ne vois aucune justification claire dans Django pour que l' POSTobjet soit immuable pour les non- multipartrequêtes.

Gareth Rees
la source
J'ai remarqué des tonnes de défauts comme celui-ci dans Django. Cela a dû avoir du sens pour quelqu'un à un moment donné, cependant.
Dan Passaro
2
J'ai trouvé ceci dans une autre réponse de Stack: "Et il doit être immuable pour pouvoir être construit paresseusement. La copie oblige à obtenir toutes les données POST. Jusqu'à la copie, il se peut qu'elles ne soient pas toutes récupérées. De plus, pour un WSGI multithread serveur pour fonctionner raisonnablement bien, c'est utile si c'est immuable "
Seaux
12
@Seaux, vous ne devriez pas lire paresseusement les réponses SO lorsque vous avez l'intention de les commenter. ;-)
Chris Wesseling
3
@ChrisWesseling Je vois ce que vous avez fait là-bas
Seaux
2
Mieux encore, le querydict est mutable lorsque j'envoie la requête en poursuivant le client de test django.
user1158559
82

Si la demande était le résultat d'une formsoumission Django , alors il est raisonnable que POST immutables'assure de l'intégrité des données entre la soumission du formulaire et la validation du formulaire . Cependant, si la demande n'a pas été envoyée via une formsoumission Django , alors POST est mutablecomme il n'y a pas de validation de formulaire.

Vous pouvez toujours faire quelque chose comme ceci: (selon le commentaire de @ leo-the-manic )

#  .....
mutable = request.POST._mutable
request.POST._mutable = True
request.POST['some_data'] = 'test data'
request.POST._mutable = mutable
# ......
un33k
la source
3
@JoshK: Je suppose que le commentateur voulait rendre POST mutable, et l'extrait de code dans cette réponse a aidé.
ShreevatsaR
Vous pouvez ajouter une nouvelle clé, une nouvelle valeur, mais vous ne pouvez pas modifier les données existantes.
Vamsidhar Muggulla
Agréable. Et je suis sûr que quiconque utilise ce code sait ce qu'il ou elle fait.
John Pang
@VamsidharMuggulla L'ajout et la modification sont possibles. Même la suppression est autorisée.
Antony Hatchkins
5

Mise à jour :

Gareth Rees avait raison de dire que les points 1 et 3 n'étaient pas valables dans ce cas. Bien que je pense que les points 2 et 4 sont toujours valables, je vais donc laisser les thèses ici.

(J'ai remarqué que l' request.POSTobjet à la fois de Pyramid (Pylon) et de Django est une forme de MultiDict. Donc, c'est peut-être une pratique plus courante que de rendre request.POSTimmuable.)


Je ne peux pas parler pour les gars de Django, bien qu'il me semble que cela pourrait pour certaines de ces raisons:

  1. La performance . les objets immuables sont «plus rapides» que les objets mutables en ce sens qu'ils permettent des optimisations substantielles. Un objet est immuable signifie que nous pouvons lui allouer de l'espace au moment de sa création , et les besoins en espace ne changent pas. Il a également des choses comme l'efficacité de copie et l'efficacité de comparaison à cause de cela. Edit : ce n'est pas le casQueryDictcomme l'a souligné Gareth Rees.
  2. Dans le cas de request.POST, il semble qu'aucune activité côté serveur ne doive modifier les données de la demande . Et par conséquent, les objets immuables sont plus adaptés, sans oublier qu'ils ont un avantage substantiel en termes de performances.
  3. Les objets immuables peuvent être utilisés comme dictclés, ce qui, je suppose, pourrait être très utile quelque part dans Django. Edit : mon erreur, immuable n'implique pas directement hashable ; Cependant, les objets hachables sont généralement également immuables .
  4. Lorsque vous passez request.POST(en particulier à des plugins tiers et à l'extérieur), vous pouvez vous attendre à ce que cet objet de requête de l'utilisateur reste inchangé.

D'une certaine manière, ces raisons sont également des réponses génériques à "immuable vs mutable?" question. Je suis certain qu'il y a beaucoup plus de considérations de conception que ci-dessus dans le cas de Django.

KZ
la source
1
Le dernier cas est vraiment important. C'est vraiment une question de sécurité. C'est pourquoi Django fournit sessionsun moyen rapide d'obtenir et de modifier des données entre les états.
CppLearner
2
Votre point (1) ne peut pas être la réponse dans ce cas, car il POSTs'agit d'un QueryDictobjet et ces objets n'obtiennent aucun avantage en termes de performances en étant immuables. Et votre point (3) ne peut pas être la réponse, car les QueryDictobjets ne sont pas hachables et ne peuvent donc pas être utilisés comme clés de dictionnaire.
Gareth Rees
@GarethRees Merci de les avoir signalés. En effet, j'avais tort. J'ai mis à jour ma réponse pour les corriger. J'aurais dû faire plus attention QueryDictavant de répondre.
KZ le
7
@CppLearner Le point de sécurité semble sans objet, par exemplerequests.POST._mutable = True; requests.POST['foo'] = 'bar'; request.POST._mutable = False
Dan Passaro
4

J'aime qu'il soit immuable par défaut. Comme indiqué, vous pouvez le rendre mutable si vous en avez besoin, mais vous devez être explicite à ce sujet. C'est comme "Je sais que je peux faire de mon débogage de formulaire un cauchemar mais je sais ce que je fais maintenant."

pawelmech
la source
2

J'ai trouvé cela dans un commentaire sur Stack Answer https://stackoverflow.com/a/2339963

Et il doit être immuable pour pouvoir être construit paresseusement. La copie force l'obtention de toutes les données POST. Jusqu'à la copie, il se peut que tout ne soit pas récupéré. De plus, pour qu'un serveur WSGI multi-thread fonctionne raisonnablement bien, il est utile si cela est immuable

Seaux
la source