Как стать автором
Обновить

Комментарии 31

Метод get, возвращающий более 2х записей, да еще и миллионы, это такой редкий кейс ошибки, вы реально ради этого джангу поманчипатчили?)
Если судить по тому, что ограничение на количество получаемых объектов методом get уже попало в официальный релиз Django 3, то получается, что я не один такой сказочный долбо§б.

Я уже в статье отметил, что считаю, что в этой функции ошибка. Ошибка в логике: функция должна получать только один объект, если он есть. Не два, не 25, и не два миллиона. Жаль только, что мой манкипатчинг (смешное слово) не исправляет эту ошибку а только уменьшает количество получаемых объектов.
метод get действительно должен возвращать один объект, но это не значит что в sql должен быть limit 1. То, что можно получить несколько записей подстраховывает от ошибок, когда метод get выбирает по неуникальному кортежу. Если вы не хотите гарантировать эту уникальность, то берите .first()/.last(). Вы не будете получать ошибок в случае неправильно сформированных параметров запроса.
По логике, данная функция должна инициировать и возвращать только один объект, и делать это только в том случае, если объект один.

В реальной Django это невозможно сделать за один запрос. Потому текущая логика метода такова:
  • Проинициализировать объекты данными из запроса, будет проинициализировано столько объектов, сколько возвращено строк,
  • Объекты сохраняются в "_result_cache".
  • Если обьект один — Get вернет ссылку на первый и единственный объект в "_result_cache"
  • GET выдаст ошибку «DoesNotExist» если _result_cache пустой
  • GET вернет MultipleObjectsReturned, при этом "_result_cache" будет заполнен несколькими объектами и их количество не учитывается.


В Django 3 появилось ограничение в 25 строк, это значит, что в sql запроса есть «LIMIT». Во всех предыдущих версиях этого ограничения не было вообще, и мы поставили у себя в проекте ограничение на количество возвращенных строк до 2х.
В этом случае будут проинициированы максимум два обьекта. После чего Get выдаст ошибки, если получен иной результат, чем один объект.

Ограничение на 2 объекта вместо 25 я предлагаю и для новой Django.
еще раз — если у вас .get() возвращает более 2х элементов, то это ошибка в коде.
то что лимит в джанге добавили — это хорошо. но между 2 и 25 разницы особой нет, а вот миллионы да, могут стрельнуть
метод get возвращал и возвращает только один объект или выдает ошибки. Ни в коде, ни в моем тексте я не вижу упоминаний что get по окончанию возвращает что-то другое.

прошу указать: где в коде или тексте статьи ошибка, подразумевающая, что результатом работы метода GET будет возврат двух и более объектов.
В итоге вы могли получить несколько миллионов объектов в памяти, только для того, чтобы узнать, что найден более, чем 1 объект.

вот же.
возвращает .get() один объект, но ему еще надо рейзить ошибку, когда из базы более 1 объекта прилетело, что свидетельствует об ошибке в логике программы.
Речь идет о методе GET в родном коде django (django/db/models/query.py). Я описал как он работает:
Объекты сохраняются в "_result_cache".
Если обьект один — Get вернет ссылку на первый и единственный объект в "_result_cache"
GET выдаст ошибку «DoesNotExist» если _result_cache пустой
GET вернет MultipleObjectsReturned, при этом "_result_cache" будет заполнен несколькими объектами и их количество не учитывается.
_result_cache хранится в памяти, методом GET не возвращается.
все верно. а чем противоречие?
Противоречие в том, что моя фраза «В итоге вы могли получить несколько миллионов объектов в памяти, только для того, чтобы узнать, что найден более, чем 1 объект.» не говорит, что результатом работы метода GET будет возврат двух и более объектов.

однако именно она была приведена с комментарием «вот же», как пример того, что результатом работы метода GET будет возврат более одного объекта.
Мне кажется изначально вам о другом говорилось, если у вас постоянно вылетает MultipleObjectsReturned, то надо не заниматься оптимизацией обработки этой ошибки, а правильно построить запросы или брать элемент при помощи .first().
Если get возвращает миллион записей а вы ожидаете одну(иначе почему get используется), то наверно не в get проблема
«Если get возвращает миллион записей...» — get возвращает один объект или ошибки. В процессе работы GET может создать много объектов на основе записей, возвращенных из базы.

Эта логическая ошибка в стандартном методе get заключается в том, что на 9 строке метода создаются объекты, которые, если их можно было создать больше одного — создаваться не должны были в принципе. А они создаются, и только потом проверяется их количество.

Кстати, когда выпал MultipleObjectsReturned, на обработке ошибки воспользоваться этими УЖЕ созданными «MultipleObjects» нельзя.
Я если честно ниче не понял, вы то пишите get, то GET. для меня это разные вещи, так как «GET» относится к методу get в CBV, а «get» это метод выборки в query. Приведите пример?

Вы делаете 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?

Я ждал этого вопроса. Наличие get не принуждает нас использовать его везде где только можно.
Оправдано оно там где мы ожидаем 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.
danilovmy

В коде метода get ошибка логики.

Это я вижу, я думал что есть другие причины, помимо внутреннего перфекционизма исправлять это.
mayorovp
а не требовать объяснить

ну не то чтоб требовал)
Вот у нас оказались два пользователя с ником swelf. Вы бы предпочли узнать об этом при первом же входе на хабр

А я и узнаю, точнее администратор ресурса узнает по ошибке. но в случае 2х пользователей будет ли такая дикая просадка по производительности, что ее кто-то заметит и будет смотреть, а что же там такое. Думаю возникнет вопрос, а как вообще так вышло, что у нас 2 пользователя с одним никнеймом, хотя казалось бы они должны быть уникальными. Вообщем о причинах уже написал ТС, просто перфекционизм)
Так вот, есть такой принцип — fail fast

Ну а в случае с методом get разве есть попытка избежать fail fast? Насколько я понимаю принцип говорит «При ошибке — падайте, не пытайтесь понять и исправить» Джанго и падает, просто падает медленно.

Я тогда могу заметить, что есть понятие «преждевременная оптимизация», незачем переопределять метод get, если может быть когда-нибудь у нас будет такая архитектурная ошибка в базе данных, что исключение MultipleObjectsReturned сожрет у нас приличное кол-во ресурсов.
Джанго и падает, просто падает медленно.

Принцип не просто так называется называется fail fast

50% гениальность / 50% тупость.

К сожалению вся джанга такая. Админка подходит только для стандартных задач, кастомизируется через жуткие костыли. Метод get вообще перестали использовать, только first. Да много чего хотелось бы поменять, поэтому вопрос, не пробовали пропихнуть свои изменения в саму джангу вместо очередной батарейки?
Админка подходит только для стандартных задач, кастомизируется через жуткие костыли.

Будь моя воля, я бы вообще запретил использовать админку джанги для чего-то нестандартного. А то накидают костылей, а потом сиди и разгребай все это в попытках обновится на следующую версию.
Так и сидим на 1.5 в одном проекте — фичи нужно завозить, а на перепиливание админки времени нет (там используются неподдерживаемые более модули на JS).
Пробовал. Десять открытых тикетов на сайте Джанго проекта и один несостоявшийся пулл реквест про конвертацию флоат/децимал- писал в предыдущей статье. В итоге плюнул, правим в нашем проекте под себя.
Пробовал. Десять открытых тикетов на сайте Джанго проекта и один несостоявшийся пулл реквест про конвертацию флоат/децимал- писал в предыдущей статье. В итоге плюнул, правим в нашем проекте под себя.
к сожалению, заметил, что развитие Django сильно замедлилось

Хм, в Django 3.0 же добавили поддержку асинхронности, разве это не большой шаг вперед? Я, правда, еще не смотрел как это выглядит, может быть это просто маркетинговый ход и работать с этим невозможно?
Правильней, сделали первый маленький шажок для добавления асинхронности, работы там дофига, есть целая статья рассказывающая что и как
По поводу замедления развития…
Когда у средней руки проекта пявляется минорный патч, он всегда выглядит революционно. Когда проект развился в нечто большое, минорными патчами уже никого не удивишь.
Django 3.0 — начато переписывание полностью синхронной Django под асинхронность. И уже вполне юзаемо, хотя это только начало. Как по мне — неплохо.
Хотелось бы узнать, что такого революционного должно появиться в Django, чтобы автор (или согласные с ним комментирующие) решил, что это — достойное развитие? Только без фраз типа «сделайте нормальную ORM». А то если дойдем до вкусовщины и каждый найдет свой любимый рецепт для решения той или иной задачи.
что меня удивит — лежит в зоне правки нескольких объектов:
Возможность инлайнов в середине фиелдсета формы админпанели.
(Правится переопределением change_form)
Возможность переменных фиелдсетов в формсетах (Правится переопределением __iter__ филдсета)
Асинхронные формсеты (в текущей реализации невозможно)
Очень удивит, если появится документация по объекту query
Отказ от жесткой привязки к jquery тоже порадует.
То что менеджер шаблонов сделали асинхронным… как то все равно. Или там еще что-то изменилось?
Хз, как по мне, так из перечисленного Вами… Ну ладно, что-то дейсствительно масштабно (про формсеты). В остальном — не то чтобы незначительные изменения, но ничего такого, что звучало бы хотя бы близко к пресловутым async во вьюхах =D.
Я не говорю, что предложенные Вами изменения несущественны. Но посудите сами: неужели инлайны в админке — то, чего ждет все комьюнити? Сделают, может. Будет этакий междудельный фикс минорный. Или сами пулл-реквестик протолкните. Тогда, если когда-нибудь увидимся, — с меня пиво =D.
Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

Публикации

Истории