Сумбурные заметки про python и django

Накопилось несколько маленьких заметок/советов про python и django, которые на отдельные топики не тянут, поэтому публикую все сразу.

Django: упрощаем код вьюх

В документации и обучающих примерах по django обычно пишут вьюхи вот так:

def contact(request):
    if request.method == 'POST':
        form = ContactForm(request.POST)
        if form.is_valid():
            # обрабатываем данные. Например, делаем form.save()
            # ...
            # после POST-запроса делаем редирект
            return HttpResponseRedirect('/thanks/')
    else:
        form = ContactForm()

    return render_to_response('contact.html', {
        'form': form,
    }, context_instance=RequestContext(request))

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

def contact(request):
    form = ContactForm(request.POST or None)
    if form.is_valid():
        # обрабатываем данные. Например, делаем form.save()
        # ...
        return redirect('url_name', param1=value)
    return direct_to_template(request, 'contact.html', {'form': form})

Фишки:

  • Для unbound-форм is_valid всегда возвращает False. Если после этого сразу непонятно, как работает код ContactForm(request.POST or None), то разберитесь в качестве упражнения, расписывать не буду. Это простая и полезная идиома.

  • Всегда используйте django.shortcuts.redirect для редиректов. Он умеет реверсить названия url'ов, вызывать get_absolute_url или просто перенаправлять по url'у.

  • Используйте django.views.generic.simple.direct_to_template вместо render_to_response. Они делают почти одно и то же, но direct_to_template использует RequestContext вместо Context, который и так нужен в большинстве случаев. Вместо direct_to_template можно использовать декоратор render_to из django-annoying, но это уже дело вкуса, кому как нравится.

    Warning

    В django 1.3 появились django.template.response.TemplateResponse и django.shortcuts.render, используйте их, а не direct_to_template.

Django: рисуем графики

В статье про админку обещал рассказать про графики, но все никак руки не доходили, нехорошо получилось. Да и рассказывать-то там особо нечего, все слишком просто и "тупо" - графики рисуются через google charts. При этом можно обойтись без всяких библиотек: конструируем себе график по вкусу тут (это полуофициальный инструмент от гугла, на него есть ссылка со справки по api google charts), а потом вставляем полученную строку в шаблон и подставляем переменные вместо тестовых значений.

Есть очень тонкая обертка над google charts: django-chart-tools. Суть — та же: собрать график визуально и заменить переменные, просто с django-chart-tools такие графики удобнее поддерживать.

Выборку данных можно делать просто через django ORM, или, для удобства/скорости, через django-qsstats-magic, в зависимости от задачи.

В итоге (с использованием django-chart-tools и django-qsstats-magic) график пользователей по дням можно вывести примерно так:

# исходные данные
qs = User.objects.filter(is_active=True)
end = datetime.today()
start = end-timedelta(days=30)

# готовим данные для графика
data = QuerySetStats(qs, 'date_joined').time_series(start, end)
values = [t[1] for t in data]
captions = [t[0].day for t in data]

потом переменные values и captions передаем в шаблон, а там выводим график таким образом:

{% load chart_tags %}
{% bar_chart values captions «580x100» %}

Ограничений по количеству обращений у google charts image api нет, там просят только связаться с ними, если > 200тыс обращений в день будет, чтобы они нагрузку распределили. Так что такие графики можно не только в админке использовать.

Django: тесты

Используйте для написания тестов django-webtest. Я уже писал про это приложение, но с того времени произошло одно очень важное изменение: django-webtest теперь предоставляет доступ к контексту шаблонов (точно так же, как и стандартный джанговский тест-клиент). Спасибо Gregor Müllegger. Теперь можно писать в таком стиле:

# ...
response = page.forms['my-form-id'].submit().follow()
assert response.context['user'] == self.user

работает также стандартный assertTemplateUsed.

django-webtest лучше любой интеграции с twill, т.к. в них нет доступа к контексту шаблонов и полной поддержки юникода, да и twill не развивается.

django-webtest лучше стандартного тест-клиента, т.к. предоставляет простой API (попробуйте-ка засабмитить форму со значениями по умолчанию через стандартный тест-клиент). Со стандартным тест-клиентом также нельзя протестировать отсутствие csrf-токена (или очень окольными пуями), а с django-webtest это делается тривиально (и даже автоматически). Используйте django-webtest)

Тут бы составить попсовую табличку с фичами: у django-webtest будут везде зеленые галочки, а у twill и стандартного тест-клиента - красные то тут, то там. Даже Ian Bicking считает, что django-webtest - это "Cool!".

Django: пишем приложение для Вконтакте

Это не просто просто, а очень просто. Отличие от обычных сайтов - только в способе регистрации и входа пользователей. Вместо django-registration ставим и настраиваем django-vkontakte-iframe. Все, теперь все посетители - это зарегистрированные и авторизованные django-пользователи, в остальном можно разрабатывать обычный сайт. Разве что еще позаботиться об js, чтобы подгонять размер iframe под размер страницы.

Python/Django: работа с русским языком

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

pymorphy вырос из статьи на хабре. Признаюсь, код сначала не был хорош, т.к. это был мой первый опыт общения как с python, так и с nlp (обработкой естественных языков). Но морфологический анализатор был-таки написан - и заброшен на год.

В начале этого года возобновил работу над pymorphy и переписал там кучу всего. А весной прошло "соревнование" морфологических анализаторов в рамках конференции Диалог-2010. Там участвовали очень серьезные ребята, результаты проверяли профессиональные лингвисты. pymorphy по дорожке "Морфология" справился лучше всех (скорее всего из-за того, что я как раз тогда выкатил работу с составными словами, записанными через дефис). Также pymorphy был единственным участником, приславшим разбор дорожки с "гразными текстами". Это все особо ни о чем не говорит, но приятно)

Python: пара трюков для выкладки пакетов на pypi

  1. В setup.py в long_description можно использовать разметку ReST. Удобно положить рядом с setup.py файлик README.rst и потом просто указать long_description = open('README.rst').read(). После этого на странице проекта на pypi сразу будет справка по нему - это просто, удобно, и в 90% устраняет необходимость в мороке с отдельной документацией.

    Note

    Тут еще такое замечание - если все же кажется, что пакету нужна документация с навигацией и тд, то стоит задуматься - возможно, пакет делает слишком много?.

  2. Есть сравнительно малоизвестный хак с setup.py. Если разметка выглядит не так, как хотелось, или исправили опечатку, или classifier, то делать новый релиз для исправления этих ошибок нет никакой необходимости: можно просто запустить python setup.py register и данные обновятся.

Python: декораторы и with

Декораторы и оператор with в питоне часто применяются для одного и того же: выполнить какие-то дополнительные действия до или после определенного куска кода. А это означает, что можно написать такую штуку, которую можно использовать одновременно и как декоратор, и как контекст-менеджер для with (например, так: http://gist.github.com/573536).

Django: принимаем платежи на сайте

Если что, через django-robokassa и django-assist-ru сделаны в продакшне тысячи покупок, > млн рублей.

Python/Django: показываем Яндекс.карту на сайте

Чтобы не возиться с геокодированием и кешированием, можно воспользоваться приложением yandex-maps.

Ух, будем считать, что все.