django-为什么request.POST对象是不可变的?


109

就像标题所问的那样,为什么Django伙计们决定通过querydict来实现request.POST对象(当然,这又使整个事情变得不可变了?)

我知道您可以通过复制帖子数据来对其进行更改

post = request.POST.copy()

但是为什么呢?当然,仅允许事物变得易变会更简单吗?还是由于其他原因使用它而可能导致问题?


1
您为什么希望它是可变的?您可以从中获取数据并在视图中使用/修改它。通过向其中添加数据,您可以创建request.POST已提交的印象,其中包含的数据比实际要多。
Simeon Visser 2012年

11
不是我希望它是可变的。我只想让冰淇淋变冷。但是对于冰淇淋来说,如果不是很冷的话,它会融化,然后您会因为制造麻烦而被责骂。但是有了request.POST对象...我的意思是,如果我要弄乱我的代码,我就要弄糟它。我没有意识到开发人员在POST对象中添加数据和导致问题的情况很普遍,因此以“修复”为目标似乎很奇怪。
bharal 2012年

好问题;从来没有真正想到过。
Burhan Khalid 2012年

1
这对我来说是偶然出现的,因为我的客户端有时会提交JSON数据(可变),有时还会提交URL表单编码(不可变)消息。
owenfi'1

2
对于非英语使用者,“ mutify”不是一个词-正确的短语是“您可以对其进行突变”或“您可以对其进行修改”。也没有必要对开发人员进行性别区分-您可以使用“ Django team”或“ core devs”,而不是“ guys”。
alexmuller '17

Answers:


131

这是一个谜,不是吗?在调查中,一些表面上似乎合理的理论是错误的:

  1. 这样POST对象就不必实现变异方法了吗?编号:该POST对象所属的django.http.QueryDict,它实现了全套的突变方法,包括__setitem____delitem__popclear。当您调用一种变异方法时,它通过检查一个标志来实现不变性。当您调用该copy方法时,您将获得另一个QueryDict实例,该实例的mutable标志处于打开状态。

  2. 为了提高性能?否:QueryDict关闭可变标志后,该类不会获得任何性能优势。

  3. 这样该POST对象可以用作字典键?否:QueryDict对象不可散列。

  4. 这样POST就可以懒惰地构建数据(无需提交读取整个响应),如此处所述?我没有看到这方面的证据代码:据我所知,整个反应总是读,无论是直接或通过MultiPartParsermultipart反应。

  5. 为了保护您免受编程错误的侵害?我已经看到了这种说法,但是从未见过对这些错误是什么以及不变性如何保护您免受错误的很好的解释。

无论如何,POST并不总是不变的:当响应为时multipart,则POST是可变的。这似乎使大多数人可能想到的理论陷入困境。(除非这种行为是疏忽。)

总之,在Django中,我看不出有明确的理由可以使POST对象对于非multipart请求是不可变的。


在Django中,我注意到了许多类似的粗糙边缘。不过在某些时候一定对某人有意义。
Dan Passaro 2014年

2
我在另一个Stack回答中找到了这一点:“而且它必须是不可变的,以便可以延迟构建。复制会强制获取所有POST数据。直到复制之前,它可能不会全部被获取。此外,对于多线程WSGI服务器可以正常工作,如果这是不可变的,这将很有帮助”
Seaux

12
@Seaux,当您打算对它们进行评论时,您不应懒洋洋地阅读SO答案。;-)
克里斯·韦瑟林

3
@ChrisWesseling我明白你在那做什么
Seaux

2
更好的是,当我通过django测试客户端发送请求时,querydict是可变的。
user1158559'3

82

如果请求是Django form提交的结果,则POST合理的做法是immutable确保表单提交和表单验证之间的数据完整性。但是,如果该请求不是通过Django 提交发送的,则POST是因为没有表单验证。formmutable

您可以随时执行以下操作:(按照@ leo-the-manic的评论

#  .....
mutable = request.POST._mutable
request.POST._mutable = True
request.POST['some_data'] = 'test data'
request.POST._mutable = mutable
# ......

3
@JoshK:我想评论者想使POST可变,此答案中的代码段很有帮助。
ShreevatsaR 2015年

您可以添加新的键,值,但不能更改现有数据。
Vamsidhar Muggulla

真好 而且我确定使用此代码的人都知道他或她在做什么。

@VamsidharMuggulla都可以添加和更改。甚至允许删除。
安东尼·哈奇金斯

5

更新

Gareth Rees是正确的,认为第1点和第3点在这种情况下无效。尽管我认为第2点和第4点仍然有效,所以我将把这些留在这里。

(我注意到request.POSTPyramid(Pylon)和Django 的对象都是某种形式MultiDict。因此,也许比使request.POST不可变更普遍。)


我无法代表Django员工发言,尽管在我看来这可能是由于以下一些原因:

  1. 表现。不变的对象比可变的对象“更快”,因为它们允许进行实质性的优化。对象是不可变的,这意味着我们可以在创建时为其分配空间,并且空间需求没有改变。因此,它还具有诸如复制效率和比较效率之类的功能。 编辑QueryDict正如Gareth Rees所指出的,情况并非如此。
  2. 在的情况下request.POST,似乎服务器端没有任何活动需要更改请求的数据。因此,不可变对象更适合,更不用说它们具有实质性的性能优势。
  3. 不可变对象可以用作dict键,我这在Django中可能非常有用。 编辑:我的错误,不可变并不直接暗示可哈希;但是,可哈希对象通常也是不可变的
  4. 当您传递request.POST(尤其是传递给第三方插件)时,您可以期望来自用户的此请求对象将保持不变。

从某种程度上说,这些原因也是“不变与可变?”的通用答案。题。我敢肯定,在Django案例中,还有比以上更多的设计注意事项。


1
最后一种情况非常重要。这实际上是关于安全性的。这就是为什么Django提供了sessions一种在状态之间获取和修改数据的短期方法。
CppLearner 2012年

2
在这种情况下,您的观点(1)不能成为答案,因为它POST是一个QueryDictobject,并且这些对象由于不可变而没有任何性能优势。而且,您的观点(3)不能解决问题,因为QueryDict对象不可哈希,因此不能用作字典键。
Gareth Rees 2012年

@GarethRees感谢您指出这些。的确,我错了。我更新了答案以更正这些问题。QueryDict在回答之前,我应该更加注意。
KZ 2012年

7
@CppLearner安全点似乎没有意义,例如requests.POST._mutable = True; requests.POST['foo'] = 'bar'; request.POST._mutable = False
Dan Passaro

4

我喜欢默认情况下它是不可变的。如前所述,如果需要,可以使它可变,但必须对此明确。就像“我知道我可以使自己的表单调试噩梦,但我知道我现在在做什么”。


2

我在Stack Answer https://stackoverflow.com/a/2339963的评论中找到了这个

而且它必须是不可变的,以便可以延迟构建。复制会强制获取所有POST数据。在复制之前,可能无法全部获取。此外,为了使多线程WSGI服务器正常运行,如果这是不可变的,则将很有帮助


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.