Результаты случайного запроса к базе данных Python / Django из-за того, что в методе «Значение по умолчанию является изменяемым» - PullRequest
0 голосов
/ 17 января 2019

Вчера я столкнулся с ошибкой в ​​своем приложении Django, и хотя с тех пор исправил ее, я до сих пор не понимаю ее причину и то, как я ее исправил.

Ну, на самом деле я нашел основную причину, когда писал этот вопрос, благодаря функции SO "Вопросы с похожими заголовками". См. «Наименьшее изумление» и изменчивый аргумент по умолчанию

Buuut, я до сих пор не понимаю, как это могло повлиять на мое приложение, как оно повлияло на него. Итак, давайте копаться.

У меня есть веб-страница, на которой отображается список элементов. Я запрашиваю эти элементы с помощью метода Model.get из файла views.py. Ничего необычного, в основном выборка БД из модели, вызов модели из представления и предоставление переменной с извлеченными значениями в шаблон.

При использовании неверного исходного кода я обновлял страницу, и элементы случайным образом либо появлялись, либо исчезали. Я полагал, что запрос БД либо вернет элементы, либо вернет пустой список.

Вот неверный исходный код (model.py):

  @classmethod
  def get(cls, school=None, additional_filters={}):
    if school:
      additional_filters['school'] = school

    return MyModel.objects.filter(
      **additional_filters
    )

А вот как я это исправил:

  @classmethod
  def get(cls, school=None, additional_filters=None):
    if not additional_filters:
      additional_filters = {}

    if school:
      additional_filters['school'] = school

    return MyModel.objects.filter(
      **additional_filters
    )

Я исправил это таким образом, потому что PyCharm IDE сказал мне, что что-то не так Default argument value is mutable и, поскольку я вообще не мог объяснить ошибку, я последовал ее рекомендациям.

Но я до сих пор не понимаю, почему. И даже сейчас, после прочтения «Наименьшее удивление» и изменяемого аргумента по умолчанию я все еще не знаю.

Теперь я понимаю, что additional_filters изменялись при каждом вызове из-за того, как Python обрабатывает аргументы функций по умолчанию в памяти.

Что я не объясняю, так это побочный эффект этого поведения. Почему запрос вернул либо правильные элементы, либо пустой набор? Особенно учитывая, что код не предоставил additional_filters, то есть единственный добавленный элемент в additional_filters был school, который всегда был одинаковым при каждом запросе.

Эту часть я действительно не понимаю. Все мои вызовы этого метода имели вид Model.get(request.context.school), и поскольку additional_filters является картой, а не массивом, в нем всегда должно содержаться одно и то же значение.

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

1 Ответ

0 голосов
/ 11 февраля 2019

FCGI поддерживает пул процессов. Каждый из членов пула имеет пустой dict в качестве аргумента ключевого слова по умолчанию.
Вам изначально повезло, и запросы достигают процессов с неизменным пустым голосом. Но всякий раз, когда запрос попадает в процесс, который уже изменил этот словарь, он перестает быть пустым требованием - ваши фильтры накапливаются.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...