2015-11-10 3 views
9

Мы можем инициализировать новый экземпляр Dict из списка ключей:Инициирование в QueryDict.fromkeys

>>> 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 использовать на всех, или полностью нарушена?

Если первый, как вы его используете? Если последний, почему он не был явно отключен в подклассе?

+1

Почему вы хотите использовать его вообще? –

+0

Почему бы и нет? – wim

+1

Я не могу воспроизвести эту ошибку. Кажется странным, в любом случае ни один из классов в цепочке наследования не имеет никаких аргументов для '__init __()'. – knbk

ответ

1

tl; dr QueryDict.fromkeys начнет работать в выпуске Django 1.11.

В PyCon 2016 у меня была возможность поговорить с некоторыми разработчиками Django ядра, мы согласились, что унаследованный метод нарушен и нарушает Liskov substitution principle. На одном из sprints я смог поцарапать этот зуд и исправить класс QueryDict.

Вот соответствующие commit и issue, исправления теперь объединены в мастер и already in the docs. Я использовал реализацию, аналогичную первому предложению от hynekcer.

+0

Спасибо за отличную работу по этой проблеме и за теоретическую контекст. Исправьте ссылку на * Liskov принцип замещения *. (Это должен быть google или wiki?) Проблема - отличный пример LSP, что [нет неизменяемого типа может быть подтипом изменчивого предка] (https://en.wikipedia.org/wiki/Liskov_substitution_principle#Principle), но некоторый изменчивый тип может быть подтипом неизменяемого предка. Поэтому, если мы нарушаем некоторые последствия LSP (как мы часто делаем для полезности), по крайней мере, конструкторы не должны быть разбиты. – hynekcer

1

Проблема также зависит от версии Django. Вы используете Django 1.7.x или более позднюю версию, для чего требуется параметр позиционирования query_string для QueryDict. Он зафиксирован в Django 1.8, где этот параметр является необязательным.

Вторая проблема заключается в том, что QueryDict создает по умолчанию в непреложного экземпляр, и нет никакого способа, как пройти через mutable=Truefromkeys. Ключи не могут быть добавлены к неизменяемым, и метод также не работает в 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() было бы более простой «реализацией». (EDIT: я не ожидаю, что что-то еще достаточно полезно для того, чтобы быть принятым на базу кода 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

Привет, теперь он находится в главном (похоже на вашу первую реализацию) -> https://docs.djangoproject.com/en/dev/_modules/django/http/request /#QueryDict.fromkeys – wim

0

Хотя QueryDict является диктофоном, основная проблема заключается в том, что он по умолчанию является неизменным dict, кроме того, вы не получаете очень хорошее сообщение об ошибке (я на самом деле использую 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.

+0

Нет, вы не можете этого сделать, потому что 'QueryDict' является многозначным dict. Тот факт, что ключевой «спам» присутствует дважды, будет уничтожен на стадии «dict.fromkeys» («спам», «спам», «картофель»)) – wim

Смежные вопросы