【问题标题】:django - why is the request.POST object immutable?django - 为什么 request.POST 对象是不可变的?
【发布时间】:2012-09-18 15:31:07
【问题描述】:

正如标题所问,为什么 Django 家伙决定使用 querydict 来实现 request.POST 对象(当然,这反过来又使整个事情不可变?)

我知道你可以通过复制帖子数据来mutify

post = request.POST.copy()

但是为什么要这样做呢?当然,无论如何允许事物是可变的会更简单吗?还是因为其他可能导致问题的原因而使用它?

【问题讨论】:

  • 为什么要它是可变的?您可以从中获取数据并在您的视图中使用/修改它。通过向其中添加数据,您可以给人一种印象,即request.POST 已提交的数据比实际更多。
  • 并不是我想要它是可变的。不超过,比如说,我想让冰淇淋变冷。但是,就冰淇淋而言,如果它冷,它会融化,然后你会因为弄得一团糟而被骂。但是对于 request.POST 对象......我的意思是,如果我要搞砸我的代码,我就会搞砸它。我不知道开发人员向 POST 对象添加数据并引发问题的现象很普遍,因此以“修复”为目标似乎是一件奇怪的事情。
  • 好问题;真的没想到。
  • 我偶尔会遇到这种情况,因为我的客户有时会提交 JSON 数据(可变),有时会提交 URL 表单编码(不可变)消息。
  • 对于非英语使用者,“mutify”不是一个词——正确的短语是“you can mutate it”或“you can modify it”。也不需要对开发人员进行性别区分——你可以使用“Django team”或“core devs”而不是“guys”。

标签: django post


【解决方案1】:

这有点神秘,不是吗?几个表面上看似合理的理论在调查中被证明是错误的:

  1. 这样POST 对象就不必实现变异方法了吗?否:POST对象属于django.http.QueryDict class,它实现了包括__setitem____delitem__popclear在内的全套变异方法。它通过在调用其中一种突变方法时检查标志来实现不变性。当您调用 copy 方法时,您会得到另一个打开了可变标志的 QueryDict 实例。

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

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

  4. 这样POST 数据可以懒惰地构建(无需承诺读取整个响应),as claimed here?我在代码中没有看到任何证据:据我所知,始终会读取整个响应,directly 或通过MultiPartParser 获取multipart 响应。

  5. 保护您免受编程错误的影响?我已经看到了这种说法,但我从来没有很好地解释过这些错误是什么,以及不变性如何保护您免受这些错误的影响。

在任何情况下,POST并不总是不可变的:当响应为 multipart 时,POST 是可变的。这似乎对你可能想到的大多数理论提出了质疑。 (除非这种行为是疏忽。)

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

【讨论】:

  • 我注意到在 Django 中有很多这样的粗糙边缘。不过,在某些时候一定对某人有意义。
  • 我在另一个 Stack 答案中发现了这一点:“它必须是不可变的,以便可以懒惰地构建它。复制强制获取所有 POST 数据。在复制之前,它可能不会全部被获取。此外,为了让多线程 WSGI 服务器正常工作,如果它是不可变的,这将很有帮助”
  • @Seaux 当您打算对其发表评论时,您不应该懒惰地阅读 SO 答案。 ;-)
  • @ChrisWesseling 我知道你在那里做了什么
  • 更好的是,当我发送请求 django 测试客户端时,querydict 是可变的。
【解决方案2】:

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

你总是可以这样做:(根据@leo-the-manic's comment

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

【讨论】:

  • @JoshK:我猜评论者想让 POST 可变,这个答案中的代码 sn-p 有帮助。
  • 您可以添加新的键值,但不能更改现有数据。
  • 不错。而且我确信使用此代码的人都知道他或她在做什么。
  • @VamsidharMuggulla 添加和更改都是可能的。甚至删除也是允许的。
【解决方案3】:

更新

Gareth Rees 说得对,第 1 点和第 3 点在这种情况下无效。虽然我认为第 2 点和第 4 点仍然有效,但我将把这些留在这里。

(我注意到 Pyramid(Pylon) 和 Django 的 request.POST 对象是某种形式的 MultiDict。所以也许这比使 request.POST 不可变更常见。)


我不能代表 Django 的人说话,尽管在我看来它可以因为以下一些原因:

  1. 表现。不可变对象比可变对象“更快”,因为它们允许大量优化。一个对象是不可变的,意味着我们可以在创建时为其分配空间,并且空间需求不会改变。它也因此具有复制效率和比较效率之类的东西。 罢工> 编辑:正如 Gareth Rees 指出的那样,QueryDict 的情况并非如此。
  2. request.POST 的情况下,似乎服务器端没有任何活动需要更改请求的数据。因此,不可变对象更适合,更不用说它们具有显着的性能优势。
  3. 不可变对象可以用作dict 键,我认为 在 Django 的某个地方可能会非常有用.. 编辑:我的错误,不可变并不直接暗示可散列;然而,hashable 对象通常也是不可变
  4. 当您传递request.POST(尤其是第三方插件和输出)时,您可以预期来自用户的这个请求对象将保持不变。

在某些方面,这些原因也是“不可变与可变?”的通用答案。题。我敢肯定,在 Django 案例中,设计考虑比上述更多。

【讨论】:

  • 最后一种情况真的很重要。这真的是关于安全性。这就是 Django 提供 sessions 的原因,这是一种在状态之间获取和修改数据的短期方式。
  • 在这种情况下,您的观点 (1) 不能成为答案,因为 POSTQueryDict object,并且这些对象不会从不可变中获得性能优势。而您的观点 (3) 不能成为答案,因为 QueryDict 对象不可散列,因此不能用作字典键。
  • @GarethRees 感谢您指出这些。确实我错了。我更新了我的答案以纠正这些。我应该在回复之前多关注QueryDict
  • @CppLearner 安全点似乎没有实际意义,例如requests.POST._mutable = True; requests.POST['foo'] = 'bar'; request.POST._mutable = False
【解决方案4】:

我喜欢它默认是不可变的。 正如所指出的,如果需要,您可以使其可变,但您必须明确说明它。 这就像“我知道我可以让我的表单调试成为一场噩梦,但我知道我现在在做什么。”

【讨论】:

    【解决方案5】:

    我在 Stack Answer https://stackoverflow.com/a/2339963 的评论中发现了这一点

    并且它必须是不可变的,以便可以懒惰地构建它。副本强制获取所有 POST 数据。直到副本,它可能不会全部被提取。此外,为了让多线程 WSGI 服务器正常工作,如果它是不可变的,这将很有帮助

    【讨论】:

      【解决方案6】:

      请注意:multipart 请求自 Django 1.11 起是不可变的 https://github.com/django/django/blob/stable/1.11.x/django/http/multipartparser.py#L292

      它们在以前的版本中是可变的。

      【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-02
      • 1970-01-01
      • 2019-05-13
      • 1970-01-01
      • 2019-03-26
      相关资源
      最近更新 更多