About Blog Dev | Alfa Romeo SZ Conkeror wishlist

All articles, tagged with “blog”

Watchlist: подписка на комментарии

Какое-то время назад Бурчик перевёл свой блог на Byteflow, и благодаря его стараниям в движке появилась возможность получать новые комментарии по почте. “Возможность” — это, конечно, сильно сказано, потому как получали их все прокомментировавшие без особых разбирательств. ;)

Однако, несмотря на всё несовершенство этой штуки, она сделала своё дело — заставила меня перестать бездельничать и написать функциональность, которая бы позволяла подписаться и отписаться от комментариев к определённому посту. За сегодня родилось небольшое приложение, которое следит за вашими подписками, позволяет подписаться на избранные посты, посмотреть список подписок (ух, каламбурище ;) и отписаться от желаемого.

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

P.S.А ещё я починил рекаптчу сегодня, сто лет долгой жизни разработчикам jQuery, её поломавшим.

Enjoy!

Обновления

Буквально только-только реализовал в Byteflow одну маленькую (совсем), но очень полезную штуку, подсказанную Катапом: в стандарте Atom’а есть два разных поля, использующихся для указания даты для элемента — published и updated, при этом updated - обязательное.

Раньше в updated просто кидалась дата создания поста или комментария, но сейчас всё по-другому. :) Теперь дата создания поста (которую можно изменить в админке для приведения в более адекватный вид, для создания “поста в будущем”) или комментария идёт в published, а в updated попадает дата, изменяющаяся при каждом изменении объекта. По идее, это должно заставить обновлённый объект появляться во всей своей красоте в рсс-ридерах. :-)

Enjoy! :)

OpenID server

Этот пост будет кратким — благодаря стараниям Олега в Byteflow теперь присутствует сервер OpenID. Клёво. :-)

P.S.А ещё byteflow — второй по тегу django на ohloh.net, после самой джанги. :-)

OpenID refactoring

Хорошие новости - я порядочно переделал механизм работы с опенидом в блоге. Правда, сами внутренности openidconsumer почти не подверглись издевательствам, изменения прошли в основном в коде работы с пользователем. Но зато для пользователей поменялось всё порядочно.

Во-первых, теперь после попытки логина с неизвестным блогу OpenID’ом теперь не покажет две непонятные формы “введите логин/пароль”, а молча залогинит - создав по пути нового пользователя. Правда, может выкинуть на старую страничку - если вдруг система sreg отдаст email, уже существующий в базе (если она не отдаст email, то всё пойдёт по первому сценарию без привлечения пользователя к процессу).

Во-вторых, теперь в профиле есть ссылочка на список ассоциированных с пользователем OpenID’ов, где их можно удалять и добавлять новые.

Так что жизнь становится легче и удобнее с каждым часом. :D Велкам! ;-)

Пользователь и его профиль

Известная штука, что у Django есть статическая (неизменяемая официально поощряемыми путями) модель User и костыль для дополнительных полей (которые может каким-либо образом использовать приложение) в виде настройки USER_PROFILE, указывающей на модельку-профиль. В результате использования такого костыля, если не делать дополнительных телодвижений, количество запросов возрастает (пример для данного блога, где каждому комментирующему ставится ссылка на его сайт) на число комментариев (даже не комментировавших, а комментариев!).

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

Потому, после продолжительных колебаний и сомнений, я решил сделать всё радикальнее - удалить всю модель UserProfile, применив вместо неё monkey patching к стандартной модели:

User.add_to_class('site', models.URLField(verify_exists=False, blank=True))
User.add_to_class('email_new', models.EmailField(blank=True))

User._meta.admin.fields += (
    ('Byteflow Extensions', {'fields': ('site', 'email_new')}),
    )

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

И ещё одно - спасибо Амиту, который и показал конкретно, как это сделать. ;-)

Темы

С подачи lorien‘а блог обзавёлся возможностью переопределять только нужные темплейты в темах. Всего лишь добавил загрузчик темплейтов (это оказалось очень просто), да настройку THEME - теперь первым делом темплейты ищутся в themes/{{ THEME }}/, а уж потом в templates/. :-) Ещё думаю сделать добавление стилей в зависимости от этой настройки, но не решил каким образом будет лучше. Наверное, темплейт тег, который будет проверять существование файликов со стилями кастомных и добавлять их в хидер. Или есть варианты лучше?

Ещё подумываю добавить мультиязычность. ;-) Но как это реализовать?… Пока знаю только django-multilingual и i18ndynamic. Может кто знает лучший вариант? Было бы неплохо, чтоб комментарии к разным языкам не пересекались, наверное… Или плохо? :\

Переезд

Вчера вечером заимпортировал все более-менее значимые посты из старого вордпрессовкого блога, осталось решить непонятную траблу с flatpages (сейчас почему-то работает только about :( ), и переезд можно будет считать окончательно завершённым - поставлю со старого блога редирект на этот, по возможности чтоб не промазывать по постам.

Сноски и обновление пингбека

Как я и писал, я решил добавить поддержку сносок. Оказалось это совсем просто, потому что порт Markdown на Python уже имеет такой плагин. Потому все изменения свелись к тому, что кинул mdx_markdown.py в каталог проекта и немножко переписал свою функцию text_to_html. Правда, пришлось немного поправить код самого плагина, чтобы сноски выводились так, как хочется мне, а не как придумал себе автор. :) Должен сказать, что генерация DOM в питоновском коде - то ещё уродство, лучше уж какие-то простенькие темплейты, что ли… :\

Вторая и главная новость состоит в том, что я добавил модель PingbackClient, которая запоминает те адреса, которые уже пинговались, и просто не даёт их пинговать при редактировании поста - чтоб не ждать каждый раз, пока оно пройдёт все урлы. Кроме того, это же позволит следить за тем, куда же таки дошли пинги. :) А ещё я закинул всю реализацию как отдельный проект на Google Code. :)

P.S.Кстати, замечания и дополнения привествуются. :) А то чувствую, ещё пару раз гляну на ping, и перепишу её. Уж больно ужасно вышло. :-)

Комментарии

Как-то неожиданно пришло желание таки доделать всю функциональность, которую задумывал для подтверждения комментариев (подтверждение происходит тогда, когда незалогиненный, но существующий пользователь - проверяется по емейлу - пытается добавить новый комментарий) - логин сразу во время подтверждения.

Сначала я, в лучших традициях чукотской наивности, просто попытался добавить вызов функции залогинивания после подтверждения правильности комментария и чистоты помыслов его автора, но не тут-то было! Джанга мне быстренько рассказала, кто здесь хозяин и где собственно у пользователя свойство backend?

Я понял, что двумя пальцами и асфальтом не обойдусь и пошёл читать доку. После беглого просмотра жалкой пары абзацев по предыдущей ссылке я уловил, что мне надо в любом случае применить функцию authenticate, которая и установит пользователю искомое свойство. В надежде, что я обойдусь без написания своего бекенда, решил порыться в гугле - и мои опасения, в который раз, оказались обоснованными. :-) Существует тикет, как раз по моему вопросу, закрытый больше года назад Малькольмом Трединником с объяснением: “нечего голову морочить, читайте доки и пишите бекенды, это несложно”.

На самом деле всё оказалось не просто несложно, а вообще просто бесплатно. ;-) Написание первоначальной версии бекенда (только для подтверждения комментариев) заняло у меня порядка 5 минут с ловлей блох, а доработка до более модной версии, которая теперь работает и для активации пользователя (активировался - и уже залогинен :-) заняла ещё порядка 10 минут с обдумыванием, а как же будет лучше это реализовать.

Получился небольшой кусочек кода, который к тому же ещё обладает способностью к лёгкому расширению. :-) На самом деле бекенд - это просто небольшой класс, который должен иметь два метода - authenticate, для проверки валидности пришедших мандатов1, и get_user, для получения юзера и добавления его к объекту запроса (т.е. к request).

get_user, использующийся с джанговской стандартной модельюUser (а я в блоге только ей и пользуюсь), всегда выходит одинаковый (про это немного написано в последнем абзаце), потому говорить тут не о чем. А вот authenticate получился достаточно интересным, на мой взгляд: он вызывает динамически (хе-хе, использование getattr даёт право написать это слово) необходимый метод для совершения действа (например, активирования пользователя) - почему я и написал про лёгкую расширяемость.

А теперь два слова про однообразный get_user. На самом деле, когда я писал сам бекенд, я понял только, зачем нужен authenticate, и чтоб разобраться с get_user (не хотелось опубликовывать куцый пост ;-), мне пришлось рыться в джанговском contrib.auth. Внимательное чтение легко объяснило, что к чему, но я заметил очень неприятную штуку - кучу жёстко закодированных вещей. Собственно, у меня самого такое встречается (и мне откровенно неудобно такое отсылать в репозиторий, но время часто не резиновое, и общее красивое решение некогда делать), но тут… В общем, у меня роятся мысли это поправить всё, вроде не выглядит сложным. Надеюсь, соберусь это сделать (и для диплома полезно будет ;-).

1 -

не могу подобрать русского перевода для слова “credentials” - Лингво из адекватных предлагает только “мандаты”, но меня и оно как-то не устраивает. В общем, сочетания логин и пароль, или openid, или, в данном случае, активационного ключа.

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

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

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

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

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

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

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

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

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

1 -

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

2 -

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