Связанные объекты

Сегодня наконец-то отбросил свою лень в сторону и сделал то, о чём так долго твердили большевики! :)

Перед всем этим текстом хочу сказать спасибо Диме Догадайло, который собственно всё это и сделал. Недавно он мне говорил, что хочет этот код выложить в опенсорс, но так как этого пока не произошло — я значит буду первый, благо он мне разрешил код использовать в блоге. :-)

В Джанге, как известно, встроен свой механизм аутентификации, который часто бывает довольно удобен (самое большое удобство заключается именно в его встроенности — интеграции со всем джанговским хозяйством), но имеет одну принципиальную проблему: модель пользователя самого практически нереально расширять.1 Пока из адекватных рабочих путей (т.е. даже если смотреть по сторонам, не обращая внимания на слова “гарантированный”, “официальный”, “документированный” ;) есть только создание отдельной модели — профиля (всем заинтересованным — читать пост Джеймса Беннетта, благо он хорошо описал2), но при этом сразу возникает другая проблема — профиль, живущий в отдельной модели, подтягивается к объекту юзеру в общем случае отдельным запросом.

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

Штука эта — несколько функций, которые позволяют загружать обратные связи за один запрос. Т.е. если мы получаем 5 постов в блог, а потом к ним комментарии — это выходит 2 запроса в базу, а не 6 (1 на посты и 5 на комментарии к каждому). В принципе, никакого rocket science’а нету, но я за время работы с джангой всегда изворачивался другим образом, если попадал на подобные грабли — не всегда это было лучше, но кое-как выходило. :-) И вот эти функции я использовал для подгрузки профилей к пользователям в отображении комментариев здесь, что уменьшило количество запросов на каждый пост в разы. :)

Я ещё успел натолкнуться на проблему, что рассчитано это было на что-то подобное “последним двадцати постам или картинкам” — уникальным объектам, и из-за этого кэш получал только первый объект из тех, кто его хотел. А в случае с комментариями такое не прокатывает никак, тут один и тот же человек комментирует несколько раз. :-) Это меня и заставило разобраться в коде и добавить поддержку неуникальных объектов.

Думаю, что прямо здесь код приводить особого смысла нету, но вот сами функции, а тут их использование.

P.S.После написания этого поста и игр с sup мне захотелось сделать сноски, подобные тем, что есть у Адама Гомаа. :-)


  1. мне кажется, что с помощью переделки модели юзера в создающуюся динамически эту проблему можно решить. 

  2. хотя я использую AutoOneToOneField Ивана Сагалаева, но большой роли это не играет. :) 

Pingbacks: 3

Просмотр SQL запросов, сделанных Django ORM
ects/ Но рассказать я хочу не про ухищрения, о которых и так уже рассказано, а о том, как можно удобно просматривать SQL запросы для каждой конкретной страницы сайта, сделанного на Django. Решение очевидно: надо сделать templatetag, который, будет выводить эти запросы ) Как гласит официальная, документация запросы, кото
web-brains.com, 05:45 (after 56 days)
Amazon byteflow: Пользователь и его профиль
Для улучшения ситуации можно применить разные методы, в том числе и load_related, который использовал я. Недостаток заключается в том, что об этом деле надо всегда помнить и везде его применять — неоправданное усложнение кода, имхо.
piranha.org.ua, 13:34 (after 38 days)
Amazon byteflow: Сноски и обновление пингбека
Как я и писал, я решил добавить поддержку сносок. Оказалось это совсем просто, потому что порт Markdown на Python уже имеет такой плагин. Потому все изменения свелись к тому, что кинул
piranha.org.ua, 20:13 (after 4 days)

Comments: 8 (already: 3) Comment post

А ничего, что ссылаешься на один и тот же файл без указания ревизии и строк :-)

Зафиксил ссылки. :)

”“Явное лучше неявного”“”, озвучивай замечания ;-)

Не понял, о чём речь? ;)

Alexander Solovyov , 09:30 (after 1 day)

Ммм… я имел в виду, вот эти штуки, которые я сейчас проставлял руками - 1 и 2. Не замечания, а сноски! Вчера забыл это слово. :) Пойду-ка в посте поправлю. :)

Alexander Solovyov , 10:59 (after 1 day)

Ссылка, куда “все и так знают” имеет смысл ставить для поднятия pagerank’а целевой страницы :-). И это я говорю не из-за того, что ссылка на меня :-)

А сноски у Гомаа, к сожалению, совершенно неюзабельные при чтении из ридера. Он почему-то их текст в основной фид не включает.

Иван Сагалаев , 14:22 (after 1 day)

Ссылка, куда “все и так знают” имеет смысл ставить для поднятия pagerank’а целевой страницы :-)

:)) Шутки не проходят незамеченными. :)

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

Уммм… Кстати да, но пишет он обычно большими постами, а я такое предпочитаю читать в окружении. Но вообще я больше думал не о внешнем виде (кстати, мне не сильно нравится идея справа от поста их показывать - я б лучше их посреди текста показывал), а о том, что это дело надо как-то автоматизировать. Наверное, какое-то расширение к маркдауну написать?

Alexander Solovyov , 18:09 (after 1 day)

Ссылка на код битая((

andrey-polyakov , 09:25 (after 648 days)

Починил. :)

Alexander Solovyov , 15:44 (after 648 days)

И вот эти функции я использовал для подгрузки профилей к пользователям в отображении комментариев здесь, что уменьшило количество запросов на каждый пост в разы. :)

Ты написал 300 строк кода. Зачем? Для меня — проще было бы решить задачу через словари, те же 2 запроса к базе, но 10 строк.

def comments(req, id):
    comments = Comment.object.filter(post_id=id) #first query
    user_ids = list(set([c.profile_id for c in comments]))
    profiles = {}

    for p in Profile.object.filter(id__in=user_ids): #second query
        profiles[p.pk] = p

    for c in comments:
        c.profile = profiles[c.profile_id]
Алексей , 06:08 (after 686 days)

Это ad hoc решение, а у меня — нет. Задумывалось оно не для применения на комментариях, а в совсем другом месте (вернее местах).

Alexander Solovyov , 06:52 (after 686 days)

Comment form for «Связанные объекты»

Required. 30 chars of fewer.

Required.

Comment post