初始化QueryDict.fromkeys

时间:2015-11-10 22:06:37

标签: python django

我们可以从键列表中初始化一个新的dict实例:

>>> dict.fromkeys(['spam', 'spam', 'potato'])
{'potato': None, 'spam': None}

一个querydict 是一个字典,所以这应该有效,对吗?

>>> QueryDict.fromkeys(['spam', 'spam', 'potato'])
TypeError: __init__() takes at least 2 arguments (1 given)

当然,我可以做一些像QueryDict('spam&spam&potato')这样的跛脚,但我的问题是:.fromkeys方法可以使用,还是完全破坏?

如果是前者,你如何使用它?如果是后者,为什么不在子类中明确禁用它?

3 个答案:

答案 0 :(得分:1)

问题还取决于Django版本。您使用需要QueryDict位置参数query_string的Django 1.7.x或更早版本。它在Django 1.8中修复,此参数是可选的。

第二个问题是QueryDict默认情况下会创建一个不可变的实例,并且无法通过mutable=True传递fromkeys。密钥不能添加到immutable中,并且该方法在Django 1.8中也会失败。

可以通过这种方式修复:

from django.http.request import QueryDict

class MyQueryDict(QueryDict):

    @classmethod
    def fromkeys(cls, seq, value='', encoding=None):
        result = cls('', mutable=True, encoding=encoding)
        for key in seq:
            result.appendlist(key, value)  # or result.update(key, value)
        return result

实现更复杂以反映重复键。默认值是空字符串,因为通过'potato=None&spam=None&spam=None'方法将其转换为字符串“无”,如urlencode()一样没有意义。默认结果QueryDict应与QueryDict('potato&spam&spam')相同。

提出的解决方案非常奇怪,raise NotImplemented()将是一个更直接的“实现”。 (编辑:我不希望任何更多有用的东西被Django代码库接受)但是我必须同意你,这是一个神秘消息的错误。未实现的功能通常没有记录,如果它们不需要警告说明,因为它们可以被发现很多。

其他解决方案是仅修改__init__

class MyQueryDict(QueryDict):

    def __init__(self, query_string=None, mutable=True, encoding=None):
        super(MyQueryDict, self).__init__(query_string=query_string,
                                          mutable=mutable,
                                          encoding=encoding)

因为不可变的QueryDict实例经常是不切实际的,甚至Django内部使用的一半都是可变的QueryDict实例。

答案 1 :(得分:1)

tl; dr QueryDict.fromkeys将开始在Django 1.11版本中工作。

在PyCon 2016上,我有机会与一些核心Django开发人员交谈,我们同意继承的方法被破坏并违反了Liskov substitution principle。在其中一个sprints我能够抓住这个痒并修补QueryDict类。

以下是相关的commitissue,此修补程序现已合并为主文件already in the docs。我使用了类似于hynekcer的第一个提案的实现。

答案 2 :(得分:0)

虽然QueryDict是一个字典,但主要问题是默认情况下它是一个不可变的字典,此外你还没有得到一个非常好的错误信息(我实际上是这样的使用Django 1.8+并获得更清晰的消息'此QueryDict实例是不可变的')。

那就是说,如果你要创建自己的QueryDict实例,你可以声明它是可变的:

mutable_query_dict = QueryDict(mutable=True)
# Calling fromkeys directly also throws an 'instance is immutable' error, so it looks like a subtle bug 
#! mutable_query_dict.fromkeys(['spam', 'spam', 'potato']) 
# But you can always use the update() method like this
mutable_query_dict.update(dict.fromkeys(['spam', 'spam', 'potato']))

如果你有一个预先存在的QueryDict,那么你可以创建一个它的副本,它会自动变为可变:

regular_query_dict = QueryDict()
# Calling fromkeys() or update() throws an immutable error 
#! regular_query_dict.fromkeys(['spam', 'spam', 'potato'])
#! regular_query_dict.update(dict.fromkeys(['spam', 'spam', 'potato'])) 
# Create a copy of the regular QueryDict to make it mutable
copy_of_regular_query_dict = regular_query_dict.copy()
# Calling fromkeys directly also throws an 'instance is immutable' error, so it looks like a subtle bug 
#! copy_of_regular_query_dict.fromkeys(['spam', 'spam', 'potato'])
# But you can always use the update() method like this
copy_of_regular_query_dict.update(dict.fromkeys(['spam', 'spam','potato']))

总之,在fromkeys()上调用QueryDict时,它确实存在一个非常微妙的错误。但是你可以使用update()方法克服它,并且还意识到必须使QueryDict变为可变。