Pull to refresh

Comments 23

В п.3 наверное было бы проще сразу всех авторов получить у которых post__id в qs. На практике это обычно быстрее, потому что полностью одним запросом к СУБД делается, пусть и с подзапросом.

Можете пример привести. Я не до конца понял.
posts_qs = Posts.objects.all()
authors = Authors.objects.filter(posts__id__in=posts_qs)
#  or
#  authors = Authors.objects.filter(posts__in=posts_qs)

Он сделает обычный join или подзапрос — уже и не помню. Но движок бд всяко быстрее отработает + там скорее всего будет пагинатор использоваться, а значит такой вариант больше подойдёт. Ну и список авторов будет без всяких distinct выдавать только уникальные записи.

Либо я не понимаю гениальности этих решений, либо автор и комментатор чего-то не понимают в джанго:
Authors.objects.filter(posts__id__isnull=False).distinct() — все авторы
Authors.objects.filter(posts__title__icontains=«кодзима гений»).delete() — выборка авторов по определенному тексту в заголовке поста.
Либо я не понимаю гениальности этих решений, либо автор и комментатор чего-то не понимают в джанго...

Либо вам чуждо понятие примера. Вместо .all() может быть любой фильтр и тогда первый пример сразу не подходит. Если параметров для поста довольно много, то можно получить не очень читабельный код, да и оптимизации это не прибавит — SQL будет либо таким же, либо около того.
Но если запрос тупо на всех авторов, которые имеют хотя бы один пост, то первый ваш пример самый читабельный. Если нужно отобрать только по содержимому заголовка, то второй тоже неплох. Но если говорить о реальном приложении, то очень редко происходит, что нужно получить данные не с уже имеющегося объекта qs.
Хотя конечно же нужно было мне написать пояснение к моему ходу мыли, чтобы неокрепшие умы не сломались. И вообще, оптимизировать надо исходя из задачи и т.д.

Хороший пример)
Но Вы же понимаете, что это исключительно в качестве примера)
Спасибо, годно как памятка для начинающих и чеклист для всех в целом.
Я буду использовать django-extentions, чтобы получить полезную информацию с помощю с

Вы же это только для логгирования SQL запросов используете (другого применения в статье не нашёл). В таком случае можно просто настроить логгинг в settings.py, сократить список зависимостей, и не вынуждать неподготовленные умы устанавливать лишнее.


LOGGING = {
    'loggers': {
        'django.db': {
            'level': 'DEBUG',
            'handlers': ['console', 'file'],
        },
        ...
    }
    ...
}

Вывод получается почти такой же (за отсутствием форматирования). Тут вам и SQL и Execution time (0.024 в скобках — как раз эта самая цифра):


>>> from myapp.models import Post
>>> Post.objects.all()
DEBUG 2020-05-24 13:52:49,540 django.db.backends utils (0.024) SELECT "myapp_post"."id", "myapp_post"."created_date", "myapp_post"."user_id" FROM "myapp_post" ORDER BY "myapp_post"."created_date" ASC  LIMIT 21; args=()
<InheritanceQuerySet []>
>>>

В остальном не увидел ничего интересного. Вся статья с громким и довольно общим заголовком — как пара абзацев из этой страницы документации: https://docs.djangoproject.com/en/3.0/ref/models/querysets/


Но кроме этого в документации есть отдельная страничка про способы оптимизации, и уж там гораздо больше интересных приёмов для статьи с таким заголовком: https://docs.djangoproject.com/en/3.0/topics/db/optimization/

Ну статья непосредственно про запросы. Я и не претендовал на то, чтобы показать какие то возможности этого пакета))) Я так же использую логирование… но я хотел сделать пост максимально сжатым и информативным.
UFO just landed and posted this here

В статье про оптимизацию запросов эта информация лишняя, и в некоторых случаях — деструктивная. Статья не одноразовая, и если завтра с этой зависимостью что-то случится (как это бывает с любой лишней зависимостью, не имеющей отношения к основной теме) — то у новичка который сюда придёт будет +1 проблема: как установить django-extensions. А ведь есть решение из коробки, которое в контексте работы с джангой было бы просто изначально надежнее.

UFO just landed and posted this here
ахах, это точно)
Я думаю что столько критики в адрес постов это уже норма на Хабре)
UFO just landed and posted this here
Та я и сам алхимию использую. Но зачастую начинающие используют ОРМ и я думаю, что статья такого рода поможет многим начать с меньшим количеством гавнокода)
О каком кешировании в п.1 идет речь, если вы обращаетесь к полю таблицы в которую делаете запрос (в втором случае)?
Как по мне на примере показано. Когда мы хотим получить доступ к ID через ForeignKey мы можем использовать кешированный ID, с помощью <field_name>_id

Не вводите людей в заблуждение — это не кеш. Вы используете поиск по полю для который ограничен внешним ключем (foreign key constraint). Это не обязывает вас использовать соединение (join) с таблицей для которой он предназначен. Чем меньше данных должна обработать СУБД, тем быстрее вы получаете результат.

Вот же знатные Django-воды на начинающего накинулись. Видно же что зеленый ещё.

Теперь по делу:

Автор, не поверишь, у тебя str(blog_obj) или str(author_obj) в некоторых случаях будут работать неправльно. Прелагаю тебе найти ошибку самостоятельно.

Post.objects.first().blog.id можно сделать не только как Post.objects.first().blog_id а еще и Post.objects.values_list('blog__id', flat=True)[0] как и Post.objects.values_list('blog_id', flat=True)[0] как и Post.objects.first().only('blog__id').blog_id и еще много вариантов, появление каждого в проекте имеет свою причину. Попробуй померить время с only.

Кроме этого не факт, что поле связи с блогом будет иметь форму «rel_obj»_id, я бы сказал, что это наиболее часто встречающийся случай и только.

Использование django-extensions для queryset explain не нужно, и продолжает использоваться только по причине отвратительной документации Django в целом и отсутствия документации по объекту query в частности. query является обязательным аттрибутом queryset. В моей статье про djangoconf 2019 есть ссылка на видео про ORM, там докладчик про explain рассказывает.

Кстати, статья про left join на хабре, что автор этой статьи прокомментировал, появилась только потому, что автор «left join» так и не понял, что сделать любой join возможно, правильно использовав queryset.query.

Использование «all()» оправдано только в применении к object_manager, т.е. .objects.all() или когда не известно, что придёт в метод — менеджер или queryset.
Если любой метод возвращающий queryset применен, то all() излишен.

В остальном согласен с комментаторами, в документации информации побольше.
Sign up to leave a comment.

Articles