Читать свежую документацию вредно

Еще когда до выхода Django 1.3 оставалось месяцев пять, я начал пытаться использовать ее для разработки коммерческих проектов. В принципе понятно, что это плохо. Но когда не хочется, либо нет времени просто заниматься ерундой, а новые “фичи” попробовать ох как хочется, приходится чем-то жертвовать. Вот и я жертвовал отчасти своим временем, отчасти временем клиентов, которое растрачивалось на переписывания готовых кусков проектов по мере изменений, вносимых в альфу.

Помню как приходили к текущим названиям переменных STATIC_URL и STATIC_ROOT. Также помню как приходилось чинить django-grappelli для работы с новой админкой.

И вот долгожданное событие наступило. 23 марта 2011 года случился релиз 1.3. Можно вздохнуть полной грудью. Больше не нужно будет переписывать куски сайтов, делая svn up в директории Django. Очередной раз я даю себе обещание не пользовать trunk для работы, а сидеть на стабильном релизе до выхода очередного стабильного релиза.

Еще параллельно заставляю себя пользоваться Class Based Views, появившихся в новой Django 1.3. Это вообще спорный момент. С одной стороны удобно и красиво, но претензий у меня к ним крайне много. Будет настроение высказаться — напишу.

Итак возвращаемся к теме статьи. Нужно мне было описать в urls.py один адрес, по которому будет осуществляться переход на другой адрес. Как эта простейшая задача может служить поводом к написанию всего этого текста? Давайте разберемся.

Имеем такой urls.py

from django.conf.urls.defaults import patterns, include, url
from anthropometry.views import MeasurementForm

urlpatterns = patterns(
	'',
	url(r'^$', MeasurementForm.as_view(),
		name='anthropometry_measurement_create'),
)

Нужно добавить еще один адрес ^user/$ который будет перебрасывать на этот корневой view под именем anthropometry_measurement_create.

Какой в Django 1.3 правильный путь редиректа с одного адреса на другой? Правильно! Новенький модный такой RedirectView.

Т.е. наш urls.py должен выглядеть теперь таким образом

from django.conf.urls.defaults import patterns, include, url
from django.views.generic.base import RedirectView
from anthropometry.views import MeasurementForm

urlpatterns = patterns(
	'',
	url(r'^$', MeasurementForm.as_view(), 
		name='anthropometry_measurement_create'),
	url(r'^user/$', RedirectView.as_view(url='/anthropometry/')),
)

Ой, что это за ужас? Хардкодить адреса в коде это не наш путь. Так делать нельзя, и делать мы этого не будем. У адреса, на который нам следует осуществить переход есть имя anthropometry_measurement_create, вот по этому имени мы и должны к нему обращаться. Но как это сделать из urls.py? Этот файл ведь грузится Django’й во время создания модуля URLconf, и мы не можем использовать внутри его reverse('anthropometry_measurement_create'), т.к. URLconf еще не создан и мы не имеем доступа к нему во время его создания.

Правильный путь — добавить вызов функции reverse в модуле views.py.

class MeasurementFormRedirect(RedirectView):
	def get_redirect_url(self):
		return reverse('anthropometry_measurement_create')

И наш urls.py в конечном итоге придет в такому виду

from django.conf.urls.defaults import patterns, include, url
from anthropometry.views import MeasurementForm, MeasurementFormRedirect

urlpatterns = patterns(
    '',
    url(r'^$', MeasurementForm.as_view(), 
        name='anthropometry_measurement_create'),
    url(r'^user/$', MeasurementFormRedirect.as_view()),
)

Теперь все кошерно, все работает как надо, никаких хардкодов, все по последнему слову моды и технологий.

Но не кажется ли вам здесь кое-что странное?

Ну да, конечно. Вот оно. Этот мир еще в своем уме? Для того, чтобы прописать простейшее правило редиректа с одного практически статического адреса на другой, мне понадобилось создать класс, переписать в нем один из методов и в другом файле воспользоваться этим классом. По-моему это круто, как для такой элементарной задачи. Уж лучше в конфиг nginx строку добавлю… А тем более для такого лаконичного языка как Python. Если бы я хотел генерировать сложные массивы не одной строкой, а несколькими страницами кода, я бы на Java продолжал решать свои задачи.

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

В рот мне ноги, простите за мой французский! Как вообще так случилось, что я только сейчас встретил эту фукнцию. Оказывается мир не такой плохой, как я про него думал. Эта функция позволяет обращаться к именованным адресам прямо из urls.py, т.к. непосредственное вычисления адреса будет происходить не сразу, а когда оно реально понадобится. Теперь никаких новых классов во views.py, и все записывается одной строчкой в urls.py

urlpatterns = patterns(
	'',
	url(r'^$', MeasurementForm.as_view(), 
		name='anthropometry_measurement_create'),
	url(r'^user/$', 
		RedirectView.as_view(
			url=reverse_lazy('anthropometry_measurement_create'))),
)

Вот мир и спасен.

Кажется…

Что, попробовали и у вас не заработало? Правильно! Этот паскудник Google, чтоб у него электричество в датацентрах в рандомном порядке пропадало, показал мне документацию не на стабильную версию Django 1.3, а на trunk. И эта функция доступна только настоящим джедаям. А обычные смертные продолжаю плодить классы во views.py. Сижу я теперь, и прогоняю от себя мысли вновь начать использовать trunk.

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