Showing posts with label django. Show all posts
Showing posts with label django. Show all posts

Monday, February 1, 2010

Краткий отчёт о PyCamp

Не далее как в прошлую субботу, 30 января 2010, в Киеве состоялась конференция PyCamp Kyiv, основной темой которой был язык Python и сопутствующие технологии. Мне повезло там побывать, и я получил огромное удовольствие от конференции.

Доклады

В основном, доклады были довольно интересными. Ниже представлены некоторые претензии/отзывы по докладам, а также оценка качества повествования/слайдов:
  • Александр Шигин "Почему Python - тормоз и как заставить его меньше тормозить" - неплохой доклад о том, какие конструкции в Python тормозят сильнее, чем другие. Для многих информация из доклада может показаться очевидной, но для меня она оказалась довольно полезной - позволила кое-как классифицировать разрознённые знания по этой теме. Качество повествования-3+, качество слайдов-5.
  • Юрий Юревич "Рецепты декораторов" - очень короткий и довольно малоинформативный доклад о том, что такое декораторы в Python. Несмотря на хорошие слайды(на 5) и доклад(на 4+)
  • Дмитрий Кожевин "Программирование на нервах" - отличный развлекательный доклад на тему управления проектами. Он небольшой и веселый, поэтому смотреть в видеозаписи обязательно. Качество доклада - 5, качество слайдов - 3.
  • Михаил Кашкин "Работа с хранилищем данных в Google App Engine, отличия от реляционной модели" - доклад сочетает в себе общий обзор Datastore и описание некоторых особенностей. Доклад, в принципе, неплохой, но неполный. После него осталось очень много вопросов. Качество слайдов - 5, качество повествования - 4.
  • Александр Соловьев "Redis: Дикий Запад баз данных" - хороший доклад про сверх-быструю key-value db. Удачно выбранная тема в данном случае на 80% определила успех доклада. Качество повествования - 5, качество слайдов - 5
  • Андрей Светлов "Безопасная разработка ПО. Результат длинного пути и множества набитых шишек" - доклад о том, как важно использовать тесты, системы автоматической сборки и прочие вспомогательные тулзы. Довольно длинно, в основном по местам давно обглоданным. К тому же для многих тема "Тестировать или не тестировать" уже далеко не так остра, как была пару лет назад. Качество повествования - 4, качество слайдов - 4.
  • Владимир Пузанов, Владимир Кириллов "Расширение и встраивание Python" - обзорный доклад об интерпретаторах Python, методах расширения стандартной функциональности и связывание с другими ЯП. Было весьма интересно, а продемонстрированный в конце хак нахожу очень приятным и довольно юзабельным. Качество повествования - 4+, качество слайдов - 3.
  • Андрей Мишковский "Использование Python в ГИС" - неплохой обзорный доклад о средствах и библиотеках для создания ГИС на Python. Хотя я бы с большим удовольствием послушал о том, как создаются ГИС, а не с помощью чего(эту информацию можно получить и в Google). Качество повествования - 5, качество слайдов - 5.
  • Сергей Кириллов "WebSockets в Twisted" - неплохой вводный доклад на тему "Что такое WebSockets?". Тема не очень сложная и была полностью раскрыта. Качество повествования - 5, качество слайдов - 5.
  • Александр Бельченко "Интернационализация и локализация Python-приложений с использованием gettext" - подробный доклад об использовании gettext для локализации GUI приложений на Python. Большую часть информации из доклада можно получить при чтении документации, но докладчик также обозначил множество интересных и полезных моментов. Качество повествования - 4+, качество слайдов - 5.
  • Иван Моргун "Работа с платежными системами в Django (PayPal, WebMoney)" - излишне подробный доклад, практически пересказ документации. Доклад можно было существенно порезать и он от этого стал бы более привлекательным. Качество повествования - 3+, качество слайдов - 3+.
  • Дмитрий Жемеров "PyCharm – новая python IDE от JetBrains" - весьма любопытная презентация новой коммерческой IDE для Python. Выглядит весьма многообещающе, но то, что оно написано на Java, а также будет в будущем стоить $100, слегка портит впечатление. Качество повествования - 5, качество слайдов :) - 5

Организация

Организована конференция была просто восхитительно: с онлайн-трансляцией, вкусными бутербродами, с вайфаем, с прямой трансляцией на большой экран сообщений из твиттера (мои сообщения). И всё это практически бесплатно. Выражаю благодарность организаторам - Максу Ищенко и Владимиру Гоцику.

Заключение

Жду видео. Побольше бы таких конференций, хороших и разных!

Wednesday, June 24, 2009

Django CouchDB backend 0.1

Не так давно стараниями 42cc была выпущена версия 0.1 бэкенда к CouchDB для Django ORM. Доступ к нему можно получитьна github, также есть трак. Так как я принимал в этом проекте весьма деятельное участие, то мне бы хотелось рассказать про него поподробнее.
Я не буду останавливаться на том, нужна или не нужна CouchDB. Вы можете прочитать об этом здесь или в google. Мне бы хотелось отметить один из недостатков CouchDB - он непривычен для людей, привыкших к реляционным базам данных, а следовательно, может показаться неудобным. Это и стало одной из причин разработки django-couchdb.
Основная цель разработки - позволить описывать взаимодействие приложений на Django с CouchDB на знакомом "языке" - на языке Django ORM. Для поверхностного использования не понадобится даже знания о том, что такое CouchDB - достаточно прописать backend в DATABASE_ENGINE и использовать его. Простейшие примеры того, что умеет django-couchdb(в виде теста):

class Boo(models.Model):
title = models.CharField(max_length=20)
slug = models.SlugField()
class Meta:
unique_together = ('title', 'slug')

class Foo(models.Model):
boo = models.ForeignKey(Boo)
boo2 = models.ForeignKey(Boo, related_name="foo2_set")

b1 = Boo(title="1", slug="1")
b1.save()
b11 = Boo(title="11", slug="1")
b11.save()
b2 = Boo(title="2", slug="2")
b2.save()
f1 = Foo(boo=b1)
f1.save()
f2 = Foo(boo=b2)
f2.save()
f3 = Foo(boo=b1,boo2=b2)
f3.save()
assert_equal(Foo.objects.filter(boo__title="1").count(), 2)
assert_equal(Foo.objects.filter(boo__title="11").count(), 0)
assert_equal(Foo.objects.filter(Q(boo__title="1") | Q(boo__slug="2")).count(), 3)

assert_equal(Foo.objects.filter(Q(boo__title="1") & Q(boo2__title="2")).count(), 1)
assert_equal(Foo.objects.filter(Q(boo__title="1") & Q(boo2__title="11")).count(), 0)

А именно: работает генерация "схемы" из моделей, insert, update, delete, select, joins(не все). Не работает - ManyToManyField, aggregates. Поэтому django.contrib(например, admin) работает, но не полностью.

Перспективы развития

Дальнейшее развитие будет вестись в нескольких направлениях:
  1. Исправление недостатков, поддержка других возможностей ORM
  2. Развитие backend для использования сильных сторон CouchDB
  3. Увеличение производительности

Выводы

Получился очень удобный инструмент, скрывающий особенности реализации (JS, HTTP запросы). Спасибо за внимание, ожидайте новые версии и новые возможности :)

Wednesday, April 1, 2009

Links: tddspry, ItQuest.ru

Link 1. tddspry

Что такое tddspry? Это небольшой набор утилит для тестирования django-приложений с помощью nosetests. Если по какой-то причине Вы не можете воспользоваться Django Test-execution Framework или джанговские тесты просто Вас раздражают, то обязательно обратите внимание на этот небольшой проект. На данный момент в состав tddspry включены хэлперы для написания тестов, а также моки для БД и twill. В дальнейшем возможно включение поддержки Windmill, etc. Проект открыт для предложений. :)
Пример использования:
class TestUI(TwillMock, DbMock):
def setup(self):
super(TestUI,self).setup()
login_to_admin(username='admin', password='admin')

@show_on_error
def test_order_add_no_transport(self): # ticket:9
go(SITE + '/shop/order/add/')
code(200)
assert not "Transport" in show(), show()

Link 2. ItQuest.ru

Возможно, многие из читателей уже слышали о таком сервисе, как ItQuest.ru. А если не слышали - обязательно обратите на него внимание. Ресурс идейно схож с StackOverflow - большая база вопросов и ответов на IT тематику. Так что если у Вас есть вопрос, ответ на который не знает даже Google, попробуйте задать его здесь.
P.S. Сайт написан на Django. Еще один повод присмотреться.

Wednesday, February 25, 2009

app-engine-patch 1.0 is out!

Не далее как 24-го февраля сего года увидела свет новая версия одной незаменимой вещи для разработки под Google AppEngine, а именно app-engine-patch.

Кажется, ничего особенного на этом скриншоте нет - всего лишь Django admin интерфейс. Однако, я не зря запостил этот скриншот - это админка Django запущенная под app-engine-patch! Теперь и под GAE можно получить эту "killer" feature Django. Об остальных нововведениях можно прочитать здесь. Меня особенно радует, что портировано django.contrib.sites, однако пока я не могу заставить django.contrib.sitemaps работать.

P.S. Еще два поинта, на которые я хотел бы указать. Во-первых, планы на будущее - "Native Django support (including Model class)."(с). Во-вторых, появилась некая тулза, которая пытается проверять импорты на правильность(кто сталкивался со страшными ошибками при случайных рекурсивных импортах - поймёт, насколько это хорошо :) )

Wednesday, January 28, 2009

Debugging with app-engine-patch and pdb

If you ever tried to use pdb in application written using app-engine-patch, you should notice, that pdb is not working here at all. All pdb output is shown directly in browser. Thanks to Antonin Hildebrand from this thread now we have a way to use pdb for debugging GAE applications. Use following code to put trace in your app:

def b():
import pdb, sys
sys.__stdout__.write('\a')
sys.__stdout__.flush()
debugger = pdb.Pdb(stdin=sys.__stdin__, stdout=sys.__stdout__)
debugger.set_trace(sys._getframe().f_back)
#...
#...
##########################
b()#here I want to start tracing
For me it worked without any dev_appserver.py patches. Try it out!

Friday, January 9, 2009

О больших IDE

Я, как и многие другие программисты, начинал свою профессиональную карьеру с позиции Windows разработчика, так что довольно долго для меня создание ПО ассоциировалось с прекрасной IDE - MS Visual Studio. Поэтому, когда я постепенно перешёл на Linux, я первым делом начал искать замену этой среде. Тогда я не очень впечатлился ни одним из соответствующих Open Source решений, потом случайно попробовал Vim, Emacs, SciTE...
Не так давно узнал, что для NetBeans IDE появилась поддержка Python. Это стало поводом посмотреть на эту IDE поближе как на возможного конкурента лидеру - Eclipse+PyDev.

Установка

Как пользователь Gentoo Linux, попробовал поставить NetBeans из portage, но меня остановил некий гнусный пакет(kaffe), который никак не хотел собираться. Более того, NetBeans тянет за собой много зависимостей. Однако, если устанавливать сборку NetBeans с сайта, всё проходит отлично. Установка дополнительного плагина для Python тоже проблем не вызывает(Tools->Plugins). Субъективное очко в пользу Eclipse, который устанавливался из portage без каких-либо проблем

Общие впечатления

Занимает чуть меньше памяти, чем Eclipse, менее глючен и падуч. Однако есть небольшие лаги в GUI. Интерфейс проще, довольно удобен. В целом, очко в пользу NetBeans

Редактор

Редактор мне понравился. Довольно удобный, есть неплохой autocomplete(который в чём-то уступает Eclipse+PyDev, но кое-в-чём его и превосходит), с автоматической подсветкой ошибок и подсказками. Ничья

Управление проектом

Всё сделано довольно просто и удобно. Менее мощно, чем у Eclipse+PyDev, но я не могу сказать, что от этого хуже. Ничья

Запуск, отладка, консоль

Консоль не очень хороша у обоих представителей. А вот с запуском и отладкой - впереди Eclipse+PyDev. В NetBeans мне так и не получилось запустить отладить Django приложение. Возможно, стоит подождать обещанной поддержки Django. Очко в пользу Eclipse

Разное

Чем меня сразу заинтересовала среда NetBeans - так это поддержкой Mercurial out-of-the-box. Субъективное очко в пользу NetBeans

Выводы

Отличная заявка на победу от NetBeans, для такого молодого плагина - очень даже ничего. Но всё же пока не закончено. С другой стороны, Eclipse+PyDev - тоже не идеал, так что я всё также буду продолжать использовать SciTE/Vim/Emacs+pdb+pylint+ipython. И ждать обновлений для NetBeans, чтобы попробовать его снова.

Wednesday, March 26, 2008

Собственные фильтры в админке django (Custom FilterSpecs)

Обновление от 04.05.2012. Данная статья была написана довольно давно, но до сих пор вызывает некоторый интерес. Спешу сообщить, что с 23-го марта 2012 года с релизом Django 1.4 начинать поиск информации про custom фильтры стоит с официальной документации

Админка django - одна из самых убойных фичей этого фреймворка, как признаются и сами авторы. Она позволяет автоматически подключить к вашему сайту функционал по добавлению, редактированию и изменению как встроенных, так и пользователских моделей. Конечно, если логика добавления и изменения информации слишком сложна, то эта админка не подходит, однако, это не означает, что ее не нужно использовать - она вполне может быть дополнением к существующему функционалу. Представьте, что создавая сайт-блог вы добавили функциональность, которая позволяет быстро создавать и редактировать записи. Но это не мешает вам использовать джанговскую админку, когда вам нужно изменить какие-то параметры записи, которые нельзя изменить иначе(подробней об админке можно прочитать в 6ой главе Djangobook)

Существуют также случаи, когда админка Django предоставляет практически весь функционал, который нужен на сайте. Например, если вам нужно приложение для внутреннего использования, которое показывает сохранённую в базе информацию по каким-нибудь событиям(пример из повседневной жизни :) ), то лучше встроенной админки не найдёшь - можно легко настроить, какие поля показывать, по каким полям вести поиск, по каким фильтровать - и всё это - лишь пара строчек кода!
Фильтры в админке - более чем удобная вещь. Например, если у вас есть поля, которые могут принимать значения из фиксированного набора(успех/не успех, номер ошибки, и т.д.), то фильтры позволяют быстро отфильтровать, например, все "успешные" записи, или все записи с упоминанием ошибки 302. Однако, может случиться, что стандартных наборов фильтров вам не хватит, и вы пожелаете добавить свой собственный.

Я постараюсь показать весь процесс добавления нового фильтра на весьма полезном примере - на фильтре, который позволит фильтровать записи по заданному промежутку времени. Например, если у вас есть поле "Время создания", то можно будет отфильтровать все записи, которые были созданы в промежутке между 25 марта 2008 года 00:00 и 26 марта 2008 года 15:00. Итак, начнём.

Все фильтры, которые есть в админке, расположены в django.contrib.admin.filterspecs, все фильтры являются наследниками FilterSpec, у которого есть два особо интересных метода - create и register. Новый фильтр нужно будет зарегистрировать, вызвав функцию FilterSpec.register(test,factory), где test - это функция, принимающая объект ..Field (фильтры создаются автоматически через интроспекцию существующих полей модели) и возвращающая True, если данный фильтр применим для данного поля, а factory - это обычно класс фильтра. Функция register() просто сохраняет эту пару во внутренний список. Функция create() вызывается, когда для какого-нибудь поля нужно создать фильтр - данная функция просматривает список, запускает test функции, и когда находит подходящий фильтр - использует factory для создания объекта и возвращает его. После объявления класса FilterSpec идут сами фильтры, которые сразу же и регистрируются.
Здесь следует важное замечание: фильтры добавляются в список append'ом, поэтому последний добавленный фильтр и будет при create вызван последним. А так как в уже рассмотренном файлике последним добавляется фильтр, который умеет фильтровать любые поля, что означает, что если мы зарегистрируем новый фильтр, то до него при create просто никогда не дойдёт очередь. Такое поведение мне не очень понятно(я привык, что последние добавленные обработчики обрабатываются первыми:) ), к тому же оно сводит на нет прямолинейные попытки добавить свой собственный фильтр.

Однако есть как минимум два способа обойти это недоразумение:
- Тикет #5883 содержит патч, который меняет порядок добавления. Он очень простой и вполне разумный. Я надеюсь, что когда-нибудь он войдёт в основную ветку :)
- Вместо register() можно вставлять нужную запись напрямую в список: FilterSpec.filter_specs.insert(-1, (test, factory))
Что использовать - ваш выбор. Я для себя выбрал путь патча, ибо хоть второй и не требует изменения кода django, однако он противоречит дзену :)

Разобравшись в том, как регистрировать фильтр, перейдём к тому, как он функционирует. Все фильтры рендерятся через шаблон filters.html (django/contrib/admin/templates/admin/filters.html). Для того, чтобы фильтр нормально отобразился, он должен уметь возвращать список опций выбора(choices), которые и будут отображены автоматически. Естественно, это не подходит для нашей цели(как вы помните, мы пытаемся сделать фильтрование по промежутку времени, для задания этого промежутка нам понадобится два текстовых поля), поэтому мы поменяем поведение шаблона(как подменить шаблон админки на свой можно прочитать всё в той же 6ой главе Djangobook). Текст шаблона filters.html ниже:

{% load admin_list %}
{% load i18n %}
{% load filter_tags %}
{% if cl.has_filters %}<div id="changelist-filter">
<h2>{% trans 'Filter' %} </h2>
{% for spec in cl.filter_specs %}
{% if spec.is_datetime_interval_filter %}
{% datetime_interval_filter cl spec %}
{% else%}
{% filter cl spec %}
{% endif %}
{% endfor %}</div>{% endif %}
Хочется обратить внимания на отличия от стандартного файла - подгружаются filter_tags - именно в нём у нас будет находиться inclusion tag datetime_interval_filter, который будет использоваться для всех фильтров, для которых is_datetime_interval_filter есть True(т.е., для нашего фильтра). Теперь посмотрим на новый тэг:
from django import template
register = template.Library()

@register.inclusion_tag('datetime_interval_filter.html')
def datetime_interval_filter(cl, spec):
return spec.get_output_dict(cl)
Ничего интересного в нём нет - он просто возвращает нужный для рендеринга контекст, оставляя процесс генерации его нашему объекту-наследнику FilterSpec(схожим образом действует и стандартный тэг filter). Как видно, он использует темплейт datetime_interval_filter.html - его текст ниже:
<script language='javascript'>
function set_new_location_{{field_name}}()
{
var lte = document.getElementById("{{field_name}}_lte_edit").value;
var gte = document.getElementById("{{field_name}}_gte_edit").value;
document.location.href = '{{query_str}}&{{field_name}}__gte='+gte+'&{{field_name}}__lte='+lte;
}
</script>
<h3>By {{field_title|escape}}:</h3>
<ul>
<li><input id="{{field_name}}_gte_edit" value="{{gte_old_value}}" /></li>
<li><input id="{{field_name}}_lte_edit" value="{{lte_old_value}}" /></li>
<li><a onClick='javascript:set_new_location_{{field_name}}();' style="cursor:pointer;">Filter</a></li>
</ul>
Что есть в темплейте - для каждого нашего фильтра создаётся небольшой скрипт, который займётся обработкой переходов на новую страницу с отфильтрованными данными. Также выводятся два текстовых поля и ссылка для перехода. Для того, чтобы корректно отрендерить этот темплейт, нужно передать:

field_name - имя поля, по которому будем фильтровать.
query_str - строка запроса, которая может содержать и другие значения фильтров.
field_title - название поля.
gte_old_value/lte_old_value - сохранённые значения текстовых полей. Выводятся в те же поля после применения фильтра, что очень удобно для редактирования.

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

Ну, и наконец, сам объект-фильтр:
from django.contrib.admin.filterspecs import FilterSpec
from django.utils.encoding import iri_to_uri

class DateFieldIntervalFilterSpec(FilterSpec):
def __init__(self, f, request, params, model):
super(DateFieldIntervalFilterSpec, self).__init__(f, request, params, model)

is_datetime_interval_filter = True

def get_output_dict(self,cl):
p = cl.params.copy()
return {
'field_name': self.field.name,
'query_str': iri_to_uri(cl.get_query_string(remove=["%s__lte"%self.field.name,"%s__gte"%self.field.name])),
'gte_old_value': p.get('%s__gte'%self.field.name,''),
'lte_old_value': p.get('%s__lte'%self.field.name,''),
'field_title':self.field.verbose_name
}
Небольшие пояснения по коду - как я уже говорил выше - мы наследуемся от FilterSpec. Конструктор не представляет никакого интереса. Флажок is_datetime_interval_filter мы уже упоминали - когда рассматривали темплейт filters.html. Единственная функция, которая требует рассмотрения - это get_output_dict(), которая возвращает словарь, используемый для рендеринга конечного html кода. field_name и field_title не представляют особого интереса - мы просто возвращаем имена поля, для которого работает фильтр. query_str мы получаем из объекта ChangeList - это основной объект, который содержит все фильтры, а также предоставляет доступ к нужным этим фильтрам параметрам(найти его можно в django.contrib.admin.views.main). В качестве параметров функции мы передаём список remove - в нём находятся те значения, которые возвращать не нужно, даже если они указаны. Это позволит нам избежать повторений вида &time__lte=smth1&time__lte=smth2. gte_old_value/lte_old_value мы получаем также из ChangeList - из текущих параметров.

После этого осталось зарегистрировать новый шаблон(для тестирования это можно сделать даже в Urlconf, хотя это и может вызвать некоторые проблемы при автоматической перезагрузке девелопмент сервера):
FilterSpec.register(lambda f: isinstance(f, models.DateField),DateFieldIntervalFilterSpec)
Хочу заметить, что я подменяю все DateTimeField(мне откровенно не нравится стандартный фильтр для DateTime), в то время, как можно добавить проверку на наличие у поля дополнительного атрибута, который добавлять только тем полям в модели, для которых нужен такой фильтр.

Ну, и конечно, хотелось бы посмотреть на результат. Вот скриншшот:На скриншоте - сохраненные Cdr events от Asterisk. Если как-нибудь будет время - я постараюсь осветить тот небольшой код, который позволяет сохранять евенты от Астериска в базе - это потрясающая иллюстрация возможностей Python, Django и Twisted.

Буду рад любым фидбекам и исправлениям. :)

Tuesday, February 26, 2008

Django urlpatterns для hostname

Я не буду сейчас детально рассказывать, что такое urlpatterns в django, понадеясь на осведомлённость возможных читателей. Однако для тех, кому это внове, дам краткий пример. В Django для сопоставления url`ов коду используются конфигурационные файлы urls.py (Urlconf). Основной смысл этих файлов в подобных строчках:

(r'login/$', 'someapp.views.login')
Данная строка означает, что когда пользователь зайдёт на страницу www.example.com/login/ , то для генерации страницы вызовется функция someapp.views.login . В простейшем случаем первым параметром такой строки является регулярка, вторым - нужная функция. Заметьте, что регулярка сопоставляется только с путём, а хостнейм отбрасывается. Сами urlconf весьма удобны, но такое игнорирование хостнейма не даёт использовать стандартные джанговские подходы для реализации подобных вещей:
  • Вызов различных функций в зависимости от части хостнейма, например, pda.example.com - PDA версия сайта, blog.example.com - блог на сайте. Конечно, это можно реализовать и другими средствами, однако создание разветвлённой структуры всё же затруднено.

  • Передача параметров в функции в зависимости от хостнейма. Например, tilarids.blogspot.com - на самом деле, все такие страницы могут генерироваться одной функцией в зависимости от имени пользователя
Т.о., возникает желание получить механизм, который бы работал аналогично urlconf, но вместо пути работал бы с хостнеймом. После небольшого исследования на эту тему у меня создалось впечатление, что это так нигде и не реализовано. Отсюда и родился подобный код:
class HostnameDispatcher(object):
def __init__(self, view, myregex):
self.regex = re.compile(myregex, re.UNICODE)
self.view_func = view
def __call__(self, request, *args, **kwargs):
current_site = RequestSite(request)
match = self.regex.search(current_site.domain)
print "Current args:",args, kwargs
if match:
new_kwargs = match.groupdict()
if new_kwargs:
new_args = args
else:
new_args = match.groups() + args
new_kwargs.update(kwargs);
return self.view_func(request,*new_args,**new_kwargs)

class HostnameRegexPattern(RegexURLPattern):
def __init__(self, regex, callback, hostname_re,proto=u'http://',default_args=None, name=None):
RegexURLPattern.__init__(self,regex,callback,default_args, name)
self.hostname_regex = hostname_re
self.dispatcher = HostnameDispatcher(self.callback, self.hostname_regex)
self.old_regex = self.regex
self.regex = re.compile(u'\b'+proto+hostname_re+u'/'+regex,re.UNICODE)

def resolve(self, path):
match = self.old_regex.search(path)
if match:
kwargs = match.groupdict()
if kwargs:
args = ()
else:
args = match.groups()
kwargs.update(self.default_args)
return self.dispatcher, args, kwargs

Хочется заметить, что основными требованиями к коду были:
  • Полная совместимость со стандартными способами resolve и reverse (получения функции по адресу и адреса по функции соответственно)
  • Безболезненное встраивание в рабочую систему Django
Возможно, код несколько сумбурный - несмотря на недолгую жизнь, он успел почувствовать на себе процесс развития и переделывания. Постараюсь объяснить, что здесь зачем. Итак:
  • HostnameDispatcher - класс, который позволяет resolv`ить функцию по хостнейму, являсь некой надстройкой над этими самими функциями(вернёмся к терминологии django и будем называть их view). Например,
    disp = HostnameDispatcher(my_view,r'^(.*)$')
    создаёт на основе существующего view новый объект, который при резолве добавляет параметры из хостнейма. Удобно, быстро, но никак не поможет при reverse. А вот reverse - это как раз самая сложная, но и не менее нужная часть
  • HostnameRegexPattern - это замена стандартным урлпаттернам. resolve здесь реализуется через уже упомянутый HostnameDispatcher. А вот с reverse пришлось извратиться. В django нет хорошей возможности изменять стандартное поведение reverse. Т.о., я просто изменил существующую регулярку, и в итоге получил почти правильный результат. Единственное, выскакивает '/' в начале. Для его устранения я не придумал ничего лучше, чем сделать небольшой патч для django(в django.core.urlresolvers):
    def backspace_process(func):
    def reverse_new(viewname, urlconf=None, args=None, kwargs=None):
    ret_val = func(viewname, urlconf, args, kwargs)
    if ret_val[:4]=='/%08':
    return ret_val[4:]
    return ret_val
    return reverse_new

    def reverse(viewname, urlconf=None, args=None, kwargs=None):
    args = args or []
    kwargs = kwargs or {}
    return iri_to_uri(u'/' + get_resolver(urlconf).reverse(viewname, *args, **kwargs))

    reverse = backspace_process(reverse)
    Т.е., добавляется в начале отдельный символ(\b), который потом удаляется при надобности. Такое удаление не должно никак повлиять на другие urlpatterns
Ну и наконец: как это использовать. Нет ничего проще- записываем в urls.py в паттерны такую строчку:
HostnameRegexPattern('test/(\d+)/$', 'blog.views.test',r'^(.*)$'),
, где первый параметр - регулярка для пути, второй - функция, третий - регулярка для хостнейма. Всё! :)

Кто может предложить что-нибудь более интересное, или улучшить уже существующий код - буду весьма благодарен.

Friday, January 18, 2008

Django WEB Framework

Не так давно открыл для себя весьма интересный веб-фреймворк для разработки сайтов по концепции MVC - это Django. Радует он хорошо продуманной структурой, документацией, а также выбранным языком программирования - Python обеспечивает ощутимую при разработке мощь. В общем, первое и незабываемое впечатление о Django - этот фреймворк писался для разработчиков, а не для пользователей.

Рекомендую! :)