Comments 31
Я уже в статье отметил, что считаю, что в этой функции ошибка. Ошибка в логике: функция должна получать только один объект, если он есть. Не два, не 25, и не два миллиона. Жаль только, что мой манкипатчинг (смешное слово) не исправляет эту ошибку а только уменьшает количество получаемых объектов.
В реальной Django это невозможно сделать за один запрос. Потому текущая логика метода такова:
- Проинициализировать объекты данными из запроса, будет проинициализировано столько объектов, сколько возвращено строк,
- Объекты сохраняются в "_result_cache".
- Если обьект один — Get вернет ссылку на первый и единственный объект в "_result_cache"
- GET выдаст ошибку «DoesNotExist» если _result_cache пустой
- GET вернет MultipleObjectsReturned, при этом "_result_cache" будет заполнен несколькими объектами и их количество не учитывается.
В Django 3 появилось ограничение в 25 строк, это значит, что в sql запроса есть «LIMIT». Во всех предыдущих версиях этого ограничения не было вообще, и мы поставили у себя в проекте ограничение на количество возвращенных строк до 2х.
В этом случае будут проинициированы максимум два обьекта. После чего Get выдаст ошибки, если получен иной результат, чем один объект.
Ограничение на 2 объекта вместо 25 я предлагаю и для новой Django.
то что лимит в джанге добавили — это хорошо. но между 2 и 25 разницы особой нет, а вот миллионы да, могут стрельнуть
прошу указать: где в коде или тексте статьи ошибка, подразумевающая, что результатом работы метода GET будет возврат двух и более объектов.
В итоге вы могли получить несколько миллионов объектов в памяти, только для того, чтобы узнать, что найден более, чем 1 объект.
вот же.
возвращает .get() один объект, но ему еще надо рейзить ошибку, когда из базы более 1 объекта прилетело, что свидетельствует об ошибке в логике программы.
Объекты сохраняются в "_result_cache".
Если обьект один — Get вернет ссылку на первый и единственный объект в "_result_cache"
GET выдаст ошибку «DoesNotExist» если _result_cache пустой
GET вернет MultipleObjectsReturned, при этом "_result_cache" будет заполнен несколькими объектами и их количество не учитывается.
_result_cache хранится в памяти, методом GET не возвращается.
однако именно она была приведена с комментарием «вот же», как пример того, что результатом работы метода GET будет возврат более одного объекта.
Если get возвращает миллион записей а вы ожидаете одну(иначе почему get используется), то наверно не в get проблема
Эта логическая ошибка в стандартном методе get заключается в том, что на 9 строке метода создаются объекты, которые, если их можно было создать больше одного — создаваться не должны были в принципе. А они создаются, и только потом проверяется их количество.
Кстати, когда выпал MultipleObjectsReturned, на обработке ошибки воспользоваться этими УЖЕ созданными «MultipleObjects» нельзя.
Вы делаете Human.objects.get(age__gt=20) и ругаетесь что django перед тем как выдать ошибку создает несколько миллиардов Human инстансев?
Да это плохо, да можно уже на втором бросить исключение, но так ваша цель получить какого-то человека а не ошибку, то наверно надо переписать либо условие выборки по уникальному id, либо брать случайного человека типа order_by('?').first()
Если бы этого исключения и правда никогда не должно было происходить ("наша цель получить какого-то человека а не ошибку"), то можно было бы просто писать всегда first. Для корректного кода это равноценная замена.
Но если мы пишем get — значит, иногда всё-таки исключение происходить должно. И видеть в этом "иногда" серьёзную деградацию производительности очень не хочется.
Но если мы пишем get — значит, иногда всё-таки исключение происходить должно.
В теории я могу такое допустить, хотя и не могу представить зачем. Можно пример? А еще лучше пример, когда у нас не просто несколько объектов, а тысячи объектов попадают под наше условие в фильтре get(). Что вообще за желание такое
«Давайте использовать get и ловить MultipleObjectsReturned в нашей базе, зная что Human.objects.get(age__gt=20) будет очень сильно тупить и выуидывать нам исключение, давайте не использовать first() для таких случаев, будем страдать, ловить ошибки, но чтобы они были быстрей залезем в кишки джанго и кое чего перепишем, нам не важен корректный результат, нам нужно чтоб код работал быстрей пусть и с ошибками»
я все правильно понял?
Скажите, а в каких случаях, по-вашему, использование get оправдано? Может, его надо вообще удалить из django?
Оправдано оно там где мы ожидаем 1 единственный объект, в редких случаях 0 объектов и тогда мы превращаем это в 404 ошибку например. Т.е. выборка по уникальным полям, например номер статьи на хабре можно выбрать при помощи get, профиль пользователя по его id или юзернейму, почте и тд. Но я не представляю зачем умышленно брать get для выборки значений которые с большой вероятностью будут не уникальными, и их будет так много что эта ошибка съест приличное кол-во ресурсов.
Следующий вопрос
Может, его надо вообще удалить из django?
Думаю джанго не пострадает если удалить его совсем. т.е. сейчас поздно удалять, слишком много где он присутствует, но в теории не вижу больших проблем обойтись filter().first() везде где используется get.
С другой стороны get немного добавляет понимания, например если мы видим get, мы понимаем что объект в базе с такими параметрами фильтра должен быть один, и если их несколько то это исключительная ситуация. И либо выбран неправильный метод выборки либо у нас ошибка в базе — 2 пользователя с никнеймом swelf.
Вообщем я так и не понял, зачем использовать get, если ожидается получение большого кол-во экземпляров модели. Зачем вообще ожидать несколько объектов при использовании get. «Извините у нас 2 или больше статьи на хабре с таким названием, поэтому мы вам не покажем ни одной»
Ясно, с этого вам и надо было начинать, а не требовать объяснить разницу между 25 записями и 2мя когда вы не понимаете зачем вообще сам метод нужен...
Так вот, есть такой принцип — fail fast. Он говорит, что программа, которая обнаружила ошибку в окружении или в самой себе, должна как можно быстрее сообщить об ошибке, чтобы её можно было исправить.
Да, при этом появляется риск что программа поломается уже у пользователя — но он компенсируется тем, что при таком подходе больше багов будет отловлено на этапах отладки и тестирования.
Рассмотрим ваши примеры. Вот у нас оказались два пользователя с ником swelf. Вы бы предпочли узнать об этом при первом же входе на хабр, или когда ваш двойник словит бан, и этот бан прилетит вам? Первое неприятно, но быстро решится через поддержку, а во втором случае ошибка может так и остаться незамеченной.
— не всё.
В коде метода get ошибка логики. Еще нет программы которая использует этот метод, еще нет примера кода, а ошибка уже есть: алгоритм get создает объекты в случае, когда создавать их не должен вообще. по-моему мнению — эта ошибка. Исправить ее в джанго возможности нет. Потому есть рекомендация минимизировать последствия в тех случаях, когда действительно используется get.
В коде метода get ошибка логики.
Это я вижу, я думал что есть другие причины, помимо внутреннего перфекционизма исправлять это.
mayorovp
а не требовать объяснить
ну не то чтоб требовал)
Вот у нас оказались два пользователя с ником swelf. Вы бы предпочли узнать об этом при первом же входе на хабр
А я и узнаю, точнее администратор ресурса узнает по ошибке. но в случае 2х пользователей будет ли такая дикая просадка по производительности, что ее кто-то заметит и будет смотреть, а что же там такое. Думаю возникнет вопрос, а как вообще так вышло, что у нас 2 пользователя с одним никнеймом, хотя казалось бы они должны быть уникальными. Вообщем о причинах уже написал ТС, просто перфекционизм)
Так вот, есть такой принцип — fail fast
Ну а в случае с методом get разве есть попытка избежать fail fast? Насколько я понимаю принцип говорит «При ошибке — падайте, не пытайтесь понять и исправить» Джанго и падает, просто падает медленно.
Я тогда могу заметить, что есть понятие «преждевременная оптимизация», незачем переопределять метод get, если может быть когда-нибудь у нас будет такая архитектурная ошибка в базе данных, что исключение MultipleObjectsReturned сожрет у нас приличное кол-во ресурсов.
50% гениальность / 50% тупость.
К сожалению вся джанга такая. Админка подходит только для стандартных задач, кастомизируется через жуткие костыли. Метод
get
вообще перестали использовать, только first
. Да много чего хотелось бы поменять, поэтому вопрос, не пробовали пропихнуть свои изменения в саму джангу вместо очередной батарейки?Админка подходит только для стандартных задач, кастомизируется через жуткие костыли.
Будь моя воля, я бы вообще запретил использовать админку джанги для чего-то нестандартного. А то накидают костылей, а потом сиди и разгребай все это в попытках обновится на следующую версию.
Так и сидим на 1.5 в одном проекте — фичи нужно завозить, а на перепиливание админки времени нет (там используются неподдерживаемые более модули на JS).
к сожалению, заметил, что развитие Django сильно замедлилось
Хм, в Django 3.0 же добавили поддержку асинхронности, разве это не большой шаг вперед? Я, правда, еще не смотрел как это выглядит, может быть это просто маркетинговый ход и работать с этим невозможно?
Когда у средней руки проекта пявляется минорный патч, он всегда выглядит революционно. Когда проект развился в нечто большое, минорными патчами уже никого не удивишь.
Django 3.0 — начато переписывание полностью синхронной Django под асинхронность. И уже вполне юзаемо, хотя это только начало. Как по мне — неплохо.
Хотелось бы узнать, что такого революционного должно появиться в Django, чтобы автор (или согласные с ним комментирующие) решил, что это — достойное развитие? Только без фраз типа «сделайте нормальную ORM». А то если дойдем до вкусовщины и каждый найдет свой любимый рецепт для решения той или иной задачи.
Возможность инлайнов в середине фиелдсета формы админпанели.
(Правится переопределением change_form)
Возможность переменных фиелдсетов в формсетах (Правится переопределением __iter__ филдсета)
Асинхронные формсеты (в текущей реализации невозможно)
Очень удивит, если появится документация по объекту query
Отказ от жесткой привязки к jquery тоже порадует.
То что менеджер шаблонов сделали асинхронным… как то все равно. Или там еще что-то изменилось?
Я не говорю, что предложенные Вами изменения несущественны. Но посудите сами: неужели инлайны в админке — то, чего ждет все комьюнити? Сделают, может. Будет этакий междудельный фикс минорный. Или сами пулл-реквестик протолкните. Тогда, если когда-нибудь увидимся, — с меня пиво =D.
Нас Django вкус волнует и манит