All articles, tagged with “django”

Django cache backend для mongodb

Вчера написал mongodb бэкенд для кэш-системы в джанго. Пощупать можно на битбакете Подключил mongodb кэш для dumpz.org — кажется, работает :) Бэкенд умеет задавать записям время жизни и при попытке чтения устаревшей записи грохает её. Я без понятия насколько это всё быстро работает по сравнению с тем же кэшем в файловой системе или RDBMS. Просто очередной велосипед в рамках изучения mongodb.

Хочу на работу

Хочу на fulltime удалённую работу python/django разработчиком со стандартной занятостью в пять рабочих дней в неделю. Последний год пробовал работать во freelance режиме на odesk.com — слишком гибко для меня.

Краткое резюме можно посмотреть по адресу lorien.name/cv.html Если вкратце, то занимаюсь django с 2007 года, питон в основном применял для веб-разработок и для различных скриптов сбора и обработки информации.

Мои контакты:

  • email/jabber: lorien@lorien.name
  • skype: lorien.name

Статус проекта pybb

Зимой 08-09 я активно работал над проектом pybb, затем весной я забил на проект и вот последний месяц опять работаю :) В течении года от нескольких человек поступили существенные контрибуции, за что им большое спасибо. Правда некоторые я вырезал уже, потому что понял, что проект начинает превращаться в очередной all-in-one кухонный комбайн. Из существенного — были оторваны личные сообщения, были вырезаны левые поля типа location, site, icq и т.д. из форумного профиля, была вырезана генерация специфических вещей из главных вьюшек типа index, category, topic и перенесена в template tags, было вырезано велосипедное поле для аватарки с ресайзом и заменено на поле из пакета sorl-thumbnail, всего и не припомню. Суть этих изменений в том, что pybb должен содержать лишь базовый функционал форумного движка, который каждый уже сам сможет расширять при желании для нужд конкретного проекта.

Я подумываю также вырезать шаблоны из стандартной поставки pybb и выложить их в виде отдельного пакета. Кстати, один уже такой пакет я создал — это pybb-templates-fluxbb. Шаблоны, имитирующие вид форума fluxbb (punbb в девичестве). Большая часть шаблонов уже сделана, остались правки по мелочи и i18n. Посмотреть на то как выглядят шаблоны можно тут http://pydev.ru

Отсутствие шаблонов в базовой поставке позволит более вдумчиво разносить JS-логику и template тэги. Часть будет специфичной для шаблона, а часть будет общая для всех шаблонов т.е. будет лежать в пакете pybb. Пример общей js логики — скрипт для прорисовывания дополнительных файловых полей в форме для создания сообщения. Ещё один пример: скрипты markitup редактора.

Я не думаю останавливаться на достигнутым и надеюсь вырезать что-нибудь ещё. Возможно, генерацию email извещений — был ведь какой-то стандартный пакет, возможно, им заменю :-)

Также, я думаю, имеет смысл создать проект с именем типа pybb-standalone, который будет представлять из себя готовый django-проекты, который можно будет поднять на сервере с минимальными усилиями. От текущего варианта установки волосы дыбом встают.

Приятно, что на базе pybb работают несколько живых форумов:

Запуск django сайта через nginx + superfcgi

Я вчера перевёл все свои сайты на связку nginx + fastcgi-демон. Раньше они работали на связке nginx + apache/mod_wsgi. Даже не знаю, зачем я это сделал. Да и какая разница :o) Я сейчас просто опишу как соединять nginx с fastcgi-django-демонами через библиотеку superfcgi и надеюсь, кому-нибудь это будет полезно.

Так вот, для начала нада поставить superfcgi библиотеку. Кстати, её автор barbuza. Ставится банально: pip install superfcgi. Только не забудьте зависимости поставить предварительно. В debian надо ставить такие пакеты: gcc, python2.5-dev (если у вас 2.5 python), libfcgi-dev. Ещё надо multiprocessing либу поставить в случае python2.5, правда, Виктор говорит, что она уже автоматически ставиться. Но, если что, имейте в виду.

После установки superfcgi надо прописать ‘superfcgi’ в settings.INSTALLED_APPS. Это сделает доступной команду manage.py runserupfcgi, с помощью которой можно запустить процесс, который позволит общаться с nginx через fastcgi-интерфейс.

Проблема в том, что надо как-то контролировать этот процесс: уметь его останавливать, например. Команда manage.py runsuperfcgi этого не умеет, поэтому я использую дополнительный bash скрипт. Пощупать его можно тут: http://bitbucket.org/lorien/spawn/src/tip/spawn.sh

У меня для каждого сайта есть свой маленький spawn.sh, в котором я задаю одну переменную $SITE и затем делаю include главного spawn.sh. Например, для запуска веб-мозгов у меня такой скрипт:

#!/bin/bash
SITE="web_brains"
source /home/web/bin/spawn.sh

На всякий случай, если вдруг сайт упадёт или будет сделан rebot сервера у меня в кроне каждую минуту стартуются все сайты. Тупо, да, зато работает :D Конечно, если сайт уже запущен, то копия не запускается, в spawn.sh учтёт этот момент. Каждую минуту я пытаюсь запустить все сайты таким скриптом:

#!/bin/sh

dirs=$(find /home/web/web -maxdepth 1 -type d)
for dir in $dirs; do
    echo "Checking directory $dir"
    cd $dir;
    if [ -e spawn.sh ]; then
        echo "Found spawn.sh"
        ./spawn.sh $1
    fi
done

Кроме django-сайтов я запустил через superfcgi также один trac-проект. Для этого я сделал следующее, создал в директории проекта файл trac_wsgi.py

import os

os.environ['TRAC_ENV'] = '/web/pybb.org'
os.environ['PYTHON_EGG_CACHE'] = '/web/cache/trac/pybb.org'

import trac.web.main
app = trac.web.main.dispatch_request

Это просто создание WSGI приложения, обслуживающего trac-проект. Теперь его можно подцепить к superfcgi библиотеке. Библиотека superfcgi предоставляет команду superfcgi, которая в параметре —app принимает название модуля, где находится wsgi-приложение. Описываю я всё довольно сумбурно, но писать подробнее не могу — шибко лень :) Я просто это всё напишу здесь, потом кто-то будет искать через google, наткнётся, прочитает, что-то поймёт. Я сам иногда на свои старые посты натыкаюсь и иногда даже нахожу то, что успел забыть :D

В общем, spawn.sh файл для запуска trac такой:

#!/bin/sh
SITE="pybb"
SOCKET_FILE="/var/run/www/pybb.sock"
NOHUP="1"
COMMAND="superfcgi --workers 1 --threads 1 --app trac_wsgi.app --socket $SOCKET_FILE --path /web/pybb.org"
source /home/web/bin/spawn.sh

Django 1.1 зарелизился

Вышел релиз Django 1.1. Список новых фич и несовместимостей со старыми версиями можно и нужно прочитать тут.

Несколько фишек, которые мне приглянулись.

Опция admin класса list_editable позволяет задавать поля модели, которые можно будет редактировать прямо в списке объектов. Подробности.

Тэг {% for %} может содержать {% empty %} тэг, который выполнится в случае, когда список для итерации был пустой. Подробности с примерами тут.

Команда dumpdata позволяет теперь делать дамп конкретной модели в приложении.

Новая функция django.shortcuts.redirect упрощяет работу с редиректами. Она принимает в качестве аргумента URL или имя urlpattern или объект модели. Подробности.

Чистим сессии в django сайте

In human-computer interaction, session management is the process of keeping track of a user’s activity across sessions of interaction with the computer system … Session management is particularly useful in a web browser where a user can save all open pages and settings and restore them at a later date. To help recover from a system or application crash, pages and settings can also be restored on next run. From: Wikepedia

За работу с сессиями в Django отвечает приложение django.contrib.sessions. По умолчанию оно хранит сессии в базе данных. Django не производит автоматическую чистку этой таблицы. Это значит, что если к вам на сайт зашёл Вася, то для него создалась сессия, Вася посмотрел и закрыл вашу страничку. Если вы ничего не делали специально с базой данных, то через год информация о сессии Васи по прежнему будет храниться в базе данных. Для Коли и других пользователей, браузеры которых поддерживают cookies, ситуация аналогична :-)

Будем чистить! Django предоставляет команду manage.py cleanup — она удаляет те сессии, время жизни которых слишком большое. Нужно всего лишь вызывать по крону эту команду. Если у вас много сайтов на вашем сервере, то есть смысл написать shell скрипт. У меня каждый сайт лежит в каталоге /web/site_name/, поэтому я сделал себе такой скрипт:

#!/bin/sh
WEB_ROOT="/web"

for dir in $(find $WEB_ROOT/* -maxdepth 0 -type d); do
    cd $dir
    if [ -f $dir/manage.py ]; then
        echo "Cleaning up the $dir"
        ./manage.py cleanup
    fi  
done

Если вы никогда не чистили таблицу сессий, то команда может работать достаточно долго в первый раз. Если вам нужна скорость, просто выполните SQL DELETE запрос с условием на возраст сессии или обыкновенный TRUNCATE.

Разгоняем django сайт с помощью staticgenerator

Я неспешно переезжаю на VDS от gandi.net. Был дедик с объёмом памяти один гиг, а переезаю на 256 метров. Пока не знаю, удастся ли втиснуться. Конечно же, возникают вопросы, а можно ли убыстрить мои сайты, заставить их жрать меньше памяти. Сегодня ночью я сражался с web-brains, а точнее с byteflow, на котором они работают. Выяснилось, что более одного запроса в секунду apache/mod_wsgi демон не обрабатывает, хоть ты тресни. Я пока не понял, во что именно упирается byteflow, но это не важно в рамках данного сообщения. Решил я закешировать веб-мозги, да так шобы прям в усмерть закешировать. Как раз вчера один знакомый упоминал staticgenerator и я понял — самое время призвать на помощь его мощности статическогенерические.

Идея, лежащая в основе staticgenerator оень простая и дерзкая. Адрес веб-страницы похож на файловый путь. Когда мы запрашиваем страницу /2009/08/07/hello-world, то staticgenerator создаёт в заданном каталоге такую же структуру т.е. каталог 2009, в нём каталог 08, в нём каталог 07, в нём каталог hello-world, а в нём файл index.html, куда и записывается содержимое сгенерированной страницы. Всё это делает специальная middleware. Далее начинается самое интересное, мы настраиваем веб-сервер так, чтобы он смотрел в заданном каталоге закэшированный файл и если там он есть, то файл выдаётся пользователю и джанго-демон даже не дёргается. Вот такие пироги :)

Далее я опишу технические детали того, как я прикрутил staticgenerator конкретно к byteflow.

Для начала неплохо установить staticgenerator через easy_install

Далее идём в settings_local.py проекта и пишем там такое в конце:

# Здесь мы просим staticgenerator складывать кэш файл в каталог нужный
WEB_ROOT = '/web/cache/staticgenerator/web-brains.com'

# Это регулярные выражения. Будут кэшироваться только те страницы, которые
# удовлетворяют выражениям. Я поковырялся в urls файлах byteflow и выбрал те,
# которые относятся к непосредственному отображению данных
STATIC_GENERATOR_URLS = (
    r'^/$',
    r'^/feeds',
    r'^/\d',
    r'^/tag',
    r'^/tags',
    r'^/featured',
    r'^/author',
    r'^/sitemap\.xml',
    r'^/robots\.txt',
)

# Так можно в byteflow добавить новую middleware из setttings.py
ADDITIONAL_MIDDLEWARE= ('staticgenerator.middleware.StaticGeneratorMiddleware',)

Ещё я в settings.py добавил cache_manager в ADDITIONAL_APPS. А что такое cache_manager? А это такое приложение, которое сигналы ловит при создании нового поста или нового комментария и грохает весь кэш. Можно, конечно, хорошо подумать и написать очищение только изменившихся страниц, а можно просто грохнуть быстренько всё :) Я запихал код в cache_manager/__init__.py — таким образом я добился его выполнения при старте сайта т.е. установки нужных обработчиков сигналов.

from subprocess import Popen from staticgenerator import quick_delete

from django.db.models.signals import post_save
from django.conf import settings

from blog.models import Post
from discussion.models import CommentNode

def clear_cache(**kwargs):
    Popen('rm -rf %s/*' % settings.WEB_ROOT, shell=True)

post_save.connect(clear_cache, sender=Post)
post_save.connect(clear_cache, sender=CommentNode)

Код элементартный. Если создали Post или CommentNode — грохаем кэш!

Так, так. Вот мы сайт настроили. Осталось веб-сервер настроить. Веб-сервер у нас, конечно, nginx. У меня такой конфиг для веб-мозгов:

server { server_name .web-brains.com; access_log /var/log/www/web-brains.com-access.log; error_log /var/log/www/web-brains.com-error.log warn;

    include fastcgi_params;

    location /admin-media {
        alias /home/web/lib/django_src/django/contrib/admin/media;
    }
    location /static/ {
        root /web/web_brains;
    }

    # Нас интересует ниженарисованный кусок конфига
    # Делаем корнем для статики тот каталог, куда
    # staticgenerator кэш пишет.
    root /web/cache/staticgenerator/web-brains.com;
    location / {
        # Всё что не GET, отдаём бэкенду. У меня это apache/mod_wsgi
        if ($request_method != GET) {
            proxy_pass http://127.0.0.1:8080;
            break;
        }
        # Вот тут чуток меняем путь поиска т.к. staticgenerator
        #  пишет кэш в index.html файлы
        if (-f $request_filename/index.html) {
            rewrite (.*) $1/index.html break;
        }
        # Если файл таки не нашёлся, идём в бэкенд
        # иначе ничего не делаем и благополучно выкидываем
        # закэшированный файл пользователю
        if (!-f $request_filename) {
            proxy_pass http://127.0.0.1:8080;
            break;
        }
    }
}

Гм, ну вот и всё. Ща сохраню статью и заодно проверю, что кэш грохнулся. Я запускал ab тесты прямо на VPS. Раньше морда веб-мозгов раздавалась со скростью 1page/second, а щас 668pages/second. Клёво :)

Интегрируем django admin interface и wysiwyg редактор xinha

Недавно понадобилось мне воткнуть WYSIWYG-редактор в джанго-админку для редактирования содержимого новостей и статей. Решено было использовать xinha. Порыскав минут 10-20 в сети я так и не нашёл статью, которую читал год назад, — в ней было описано как интегрировать по-красивому сложные виджеты в админку. Пришлось придумывать самому.

Суть идеи в том, что мы указываем в классе, отвечающем за новостной admin-интерфейс, специальный класс формы, в котором используем для одного из полей специальный виджет, который собственно и подгружает WYSIWYG редактор.

Итак, для начала нужно скачать редактор xinha с сайта, распаковать архив и засунуть его содержимое, например, в /static/xinha

Далее, пускай у нас есть модель News, в которой нам нужно редактировать поле content. В news/admin.py создаём админ-класс и говорим, что хотим использовать особую форму.

class NewsAdmin(admin.ModelAdmin):
    form = NewsAdminForm

Далее, создаём особую форму.

class NewsAdminForm(forms.ModelForm):
    class Meta:
        model = News

    def __init__(self, *args, **kwargs):
        super(NewsAdminForm, self).__init__(*args, **kwargs)
        self.fields['content'].widget = XinhaWidget()

В этой форме мы меняем только виджет для поля content, всё остальное за нас делает ModelForm. Другими словами форма будет похожа на стандартную за исключением поля content.

Теперь нам нужно описать класс виджета.

from django.utils.safestring import mark_safe
from django import forms

XINHA_CODE = """
<script type="text/javascript">
var xinha_editors = ['id_content'];
</script>
"""

class XinhaWidget(forms.Textarea):
    def render(self, *args, **kwargs):
        out = super(XinhaWidget, self).render(*args, **kwargs)
        return mark_safe(XINHA_CODE + out)

    class Media:
        js = ('js/xinha_prepare.js', 'xinha/XinhaCore.js', 'js/xinha_configure.js')

Здесь мы опять же берём за основу готовую вещь — виджет Textarea и дописываем после HTML-кода маленький javascript, который изменяет глобальную переменную xinha_editors — она будет использована после полной загрузки страницы большим и жирным javascript-кодом, который подгружает и настраивает редактор xinha.

Обратите внимание на class Media. Он говорит джанге, что на странице, где используется наш виджет, нужно подключить эти скрипты (в head секции страницы).

В Media мы объявляем три скрипта. Что значит второй, думаю, всем понятно. Первый скрипт делает небольшую подготовку к загрузке xinha — объявляет язык и базовый URL:

_editor_url = ‘/static/xinha/’ _editor_lang = ‘ru’

Далее грузится второй скрипт — потроха Xinha. И в третьем скрипте мы используем подгруженные библиотеки — создаём реальные объекты редактора Xinha и настраиваем их.

У меня третий скрипт (xinha_configure.js) получился таким. Он достаточно большой (жирнючие комментарии) — я редактировал болванку из документации Xinha — так что я вынес код на dumpz.org.

Вот и всё. Теперь грузим админку и радуемся интегрированной xinha :-)

UPD: Нашёл пост, который давно читал, вот он: A WYSIWYM editor widget for Django’s admin interface

Работа

У меня есть два сообщения про работу.

1)Ищу удалённую работу. Умею делать сайты на django и писать скрипты на python. Вот здесь можно прочитать что-то типа резюме: lorien.name/resume.html

2) Сегодня с утра набросал сайтик pyjob.ru. Почти что русский клон djangogis.com, за исключением того, что я не стал делать каталог разработчиков т.е. pyjob.ru — это просто список вакансий. На данный момент список пустой. Буду прилагать усилия в направлении его наполняемости.

Если у вас есть какие-то предложения по первому или второму пункту, то мой email/jabber: lorien@lorien.name

UPD: Объявление о поиске работы уже неактуально.

Анонсы двух мероприятий в python мире.

Хочу рассказать о двух событиях, которые произойдут в ближайшее время.

На 6 июня запланирована RuPyRu 2009. Это конференция по python и ruby, ежегодно проходящая в начале лета в г.Омске. Подробности о мероприятии можно узнать из сообщения её основателя и главного организатора Юры Юревича RuPyRu 2009: ищутся участники.

Напоминаю, что для конференции создана google группа: rupyru-discuss, где можно задавать, интересующие вас вопросы.

Второе событие — это djangodash.com. Дата начала — 30 мая. Djangodash — это соревнование, суть которого сделать с нуля за 48 часов сайт на джанго. Мы там будем с sevenov одну штуку мутить. Вот наша команда Code monkeys :-)

Предлагаю всем питонерам проявить активность и по мере сил и возможностей принять участие в том или ином мероприятии :-)