在尝试创建保留QueryDict
子类的交叉兼容订单时:
from collections import OrderedDict
from django.http import QueryDict
from django.conf import settings
settings.configure()
class OrderedQueryDict(QueryDict, OrderedDict):
pass
querystring = 'z=33&x=11'
print(QueryDict(querystring).urlencode())
print(OrderedQueryDict(querystring).urlencode())
Python 3.x上的输出(正确和预期的结果):
z=33&x=11 # or maybe x=11,z=33 on Python<=3.5
z=33&x=11
Python 2.7上的输出(此查询字符串已损坏):
x=11&z=33
z=3&z=3&x=1&x=1
为什么这个想法在Python 3上有效,但在Python 2上无效?
Django v1.11.20。
答案 0 :(得分:6)
TLDR:重新实现lists
:
class OrderedQueryDict(QueryDict, OrderedDict):
def lists(self):
"""Returns a list of (key, list) pairs."""
return [(key, self.getlist(key)) for key in self]
要获得全部功能,还应该重新实现iterlists
。
问题是Django的MultiValueDict
覆盖__getitem__
来仅检索最后一个值,而getlist
检索了所有值。这隐式依赖于基础映射的其他方法,而不使用覆盖的方法。例如,它依靠super().iteritems
能够检索值列表:
>>> from django.utils.datastructures import MultiValueDict
>>> d = MultiValueDict({"k": ["v1", "v2"]})
>>> d.items()
[('k', 'v2')]
>>> super(MultiValueDict, d).items()
[('k', ['v1', 'v2'])]
original code使用six
覆盖Python 2和3。这是Python 2执行的操作:
def lists(self):
return list(self.iterlists())
def iterlists(self):
"""Yields (key, list) pairs."""
return super(MultiValueDict, self).iteritems()
在Python 2中,OrderedDict
是用纯Python实现的,并且依赖于self[key]
即__getitem__
来检索值:
def iteritems(self):
'od.iteritems -> an iterator over the (key, value) pairs in od'
for k in self:
yield (k, self[k])
这样,它会从MRO中提取覆盖的__getitem__
,并且仅返回单个值,而不返回整个列表。
在大多数Python 3.5+版本中,此问题已被规避,因为OrderedDict
通常具有可用的C实现,从而意外地屏蔽了其方法以免使用覆盖的方法。
collections.OrderedDict现在用C实现,这使其速度提高了4到100倍。[What's new in Python 3.5]