|
Оглавление
|
Содержание
Данная глава временно взята из первой версии книги и подлежит корректировке. Вы можете помочь с этим! Перевод © Попов Руслан <radz • yandex • ru> В главе «Представления и привязки URL» мы рассказали об основах функций представлений Django и о схеме URL, в которой определено соответствие URL этим функциям. Эта глава остановится на этих вопросах более подробно. В схеме URL нет ничего особенного, как всё остальное в Django, это просто код на языке Python. Вы можете использовать это знание различными способами, как показано в разделах главы далее. Рассмотрим нижеприведённый файл со схемой URL:: from django.conf.urls.defaults import *
from mysite.views import current_datetime, hours_ahead, hours_behind,
now_in_chicago, now_in_london
urlpatterns = patterns('',
(r'^now/$', current_datetime),
(r'^now/plus(\d{1,2})hours/$', hours_ahead),
(r'^now/minus(\d{1,2})hours/$', hours_behind),
(r'^now/in_chicago/$', now_in_chicago),
(r'^now/in_london/$', now_in_london),
)
Как объяснялось в главе «Представления и привязки URL», каждый шаблон схемы URL ассоциирован с функцией представления, которая указывается в виде функционального объекта. Это означает, что необходимо импортировать функции представления вначале модуля. Но по мере усложнения Django приложения, его схема URL также растёт, управление этими импортами может быть утомительным. (Для каждой новой функции представления вам потребуется не забыть об её импорте, это скажется на размере оператора импорта.) Есть возможность избежать этого, просто импортируя сам модуль views. Нижеприведённый пример эквивалентен предыдущему: from django.conf.urls.defaults import *
from mysite import views
urlpatterns = patterns('',
(r'^now/$', views.current_datetime),
(r'^now/plus(\d{1,2})hours/$', views.hours_ahead),
(r'^now/minus(\d{1,2})hours/$', views.hours_behind),
(r'^now/in_chicago/$', views.now_in_chicago),
(r'^now/in_london/$', views.now_in_london),
)
Django предоставляет другой способ указания функции представления для определённого шаблона в схеме URL. Вы можете передать строку, которая содержит имя модуля и имя функции, вместо передачи самого объекта. Продолжаем работу над нашим примером: from django.conf.urls.defaults import *
urlpatterns = patterns('',
(r'^now/$', 'mysite.views.current_datetime'),
(r'^now/plus(\d{1,2})hours/$', 'mysite.views.hours_ahead'),
(r'^now/minus(\d{1,2})hours/$', 'mysite.views.hours_behind'),
(r'^now/in_chicago/$', 'mysite.views.now_in_chicago'),
(r'^now/in_london/$', 'mysite.views.now_in_london'),
)
Следует отметить кавычки вокруг имён представления! Используя этот метод, больше нет необходимости импортировать функции представления. Django автоматически импортирует соответствующую функцию представления как только она понадобится, в соответствии с текстовым описанием её имени и пути.
Ещё сильнее код можно сократить, используя общий префикс для
пути к функциям представления. В нашем примере, каждая строка
начиналась с «mysite.views», т.е. опять
дублирование. Мы можем исключить этот общий префикс и передать
его в качестве первого аргумента функции
from django.conf.urls.defaults import *
urlpatterns = patterns('mysite.views',
(r'^now/$', 'current_datetime'),
(r'^now/plus(\d{1,2})hours/$', 'hours_ahead'),
(r'^now/minus(\d{1,2})hours/$', 'hours_behind'),
(r'^now/in_chicago/$', 'now_in_chicago'),
(r'^now/in_london/$', 'now_in_london'),
)
Следует заметить, что не надо ставить завершающую точку в префикс и не надо ставить точку перед именами функций представления. Всё это будет автоматически сделано Django. Так какой из этих двух подходов лучше? Это зависит от вашего стиля написания кода и ваших нужд. Преимущества использования строк вместо объектов:
Преимущества использования объектов вместо строк:
Оба подхода правильные. Вы можете смешивать их в одном файле, описывающем схему URL. Это ваш выбор.
На практике при использовании строчной методики вы можете
столкнуться с ситуацией, когда функции представления в схеме
URL не будут иметь общего префикса. Однако, это не помешает
вам воспользоваться преимуществами префикса. Просто добавьте
нужное количество # Раньше
from django.conf.urls.defaults import *
urlpatterns = patterns('',
(r'^/?$', 'mysite.views.archive_index'),
(r'^(\d{4})/([a-z]{3})/$', 'mysite.views.archive_month'),
(r'^tag/(\w+)/$', 'weblog.views.tag'),
)
# Теперь
from django.conf.urls.defaults import *
urlpatterns = patterns('mysite.views',
(r'^/?$', 'archive_index'),
(r'^(\d{4})/([a-z]{3})/$', 'archive_month'),
)
urlpatterns += patterns('weblog.views',
(r'^tag/(\w+)/$', 'tag'),
)
Всё, о чём заботится среда разработки, — это о
существовании переменной
Говоря о динамическом построении
from django.conf.urls.defaults import*
from django.conf import settings
urlpatterns = patterns('',
(r'^$', 'mysite.views.homepage'),
(r'^(\d{4})/([a-z]{3})/$', 'mysite.views.archive_month'),
)
if settings.DEBUG:
urlpatterns += patterns('',
(r'^debuginfo$', 'mysite.views.debug'),
)
В этом примере URL /debuginfo/ будет работать, только если параметр DEBUG установлен в True. Ранее во всех примерах мы использовали простые, неименованные группы регулярных выражения. Мы ограничивали части URL круглыми скобками, которые требовалось обработать функцией представления, как аргументы. Существует возможность использования именованных групп регулярных выражений для получения кусков URL и последующей их передачи функции представления как именованных аргументов.
Именованные аргументы и обязательные аргументыФункция в языке Python может быть вызвана с использованием именованных аргументов или с помощью обязательных аргументов, а в некоторых случаях, эти аргументы можно смешивать. При использовании именованных аргументов вы должны указать имена и значения аргументов. В противном случае вы просто передаёте значения, они будут назначены аргументам по порядку их определения в функции. Например, рассмотрим эту простую функцию: def sell(item, price, quantity):
print "Selling %s unit(s) of %s at %s" % (quantity, item, price)
Для вызова функции с помощью обязательных аргументов надо перечислить их в порядке, в котором они определены в функции: sell('Socks', '$2.50', 6)
Для вызова функции с помощью именованных аргументов следует указать имена и значения аргументов. Следующие операторы эквивалентны предыдущему примеру: sell(item='Socks', price='$2.50', quantity=6) sell(item='Socks', quantity=6, price='$2.50') sell(price='$2.50', item='Socks', quantity=6) sell(price='$2.50', quantity=6, item='Socks') sell(quantity=6, item='Socks', price='$2.50') sell(quantity=6, price='$2.50', item='Socks')
Наконец, мы можете смешивать эти два подхода, пока обязательные аргументы находятся на своих местах. Следующие операторы эквивалентны предыдущим примерам: sell('Socks', '$2.50', quantity=6)
sell('Socks', price='$2.50', quantity=6)
sell('Socks', quantity=6, price='$2.50')
В регулярных выражениях Python синтаксис для именованных групп регулярных выражений будет таким: (?P<name>pattern), где name является именем группы, а pattern — неким шаблоном. Ниже дан пример файла со схемой URL, в котором используются неименованные группы: from django.conf.urls.defaults import *
from mysite import views
urlpatterns = patterns('',
(r'^articles/(\d{4})/$', views.year_archive),
(r'^articles/(\d{4})/(\d{2})/$', views.month_archive),
)
Теперь приведём тот же файл, который переписан с использованием именованных групп: from django.conf.urls.defaults import *
from mysite import views
urlpatterns = patterns('',
(r'^articles/(?P<year>\d{4})/$', views.year_archive),
(r'^articles/(?P<year>\d{4})/(?P<month>\d{2})/$', views.month_archive),
)
Этот пример аналогичен предыдущему за исключением одного тонкого различия: полученные значения передаются в функции представления в виде именованных аргументов. Например, при использовании неименованных групп запрос к /articles/2006/03/ будет преобразован к такому вызову функции: month_archive(request, '2006', '03')
Однако при использовании именованных групп тот же самый запрос будет преобразован к такому вызову функции: month_archive(request, year='2006', month='03')
На практике, использование именованных групп позволяет вашей схеме URL быть более очевидной и менее подверженной внесению ошибок. Вы можете менять порядок следования аргументов в ваших функциях представления. Следуя вышеописанным примерам, если нам потребуется так изменить URL, чтобы вставить месяц перед годом, при использовании неименованных групп, нам потребуется не забыть внести соответствующие изменения в порядок аргументов в представлении month_archive. При использовании именованных групп такое изменение порядка параметров в регулярном выражении никак не скажется на работе функции представления. Конечно, выгода от использования именованных групп идёт от краткости. Некоторые разработчики найдут синтаксис именованных групп ужасным и слишком многословным. Другим преимуществом именованных групп является читаемость, особенно для людей, незнакомых с регулярными выражениями или с вашим Django приложением. Ведь проще всего понять, что происходит в приложении, просто заглянув в файл со схемой URL, который использует именованные функции. Недостаток использования именованных групп в схеме URL в том, что один шаблон не может содержать неименованные и именованные группы. Если вы попробуете так сделать, Django не отобразит никакой ошибки, но, вероятно, вы обнаружите, что ваша схема перестала работать так как ожидалось. Проще говоря, существует алгоритм работы парсера схемы URL в отношении именованных и неименованных групп в регулярных выражениях:
Случается, что иногда приходится писать незначительно различающиеся функции представления. Например, скажем у вас есть два представления, чьё содержимое идентично, исключая шаблоны, которые они используют: # urls.py
from django.conf.urls.defaults import *
from mysite import views
urlpatterns = patterns('',
(r'^foo/$', views.foo_view),
(r'^bar/$', views.bar_view),
)
# views.py
from django.shortcuts import render_to_response
from mysite.models import MyModel
def foo_view(request):
m_list = MyModel.objects.filter(is_new=True)
return render_to_response('template1.html', {'m_list': m_list})
def bar_view(request):
m_list = MyModel.objects.filter(is_new=True)
return render_to_response('template2.html', {'m_list': m_list})
Мы сами дублируем код и это не элегантно. Сначала вы можете решить убрать избыточность просто используя одно и то же представления для обоих URL, поставите скобки в URL для того, чтобы получить его и проверить, чтобы определить какой шаблон надо использовать, например так: # urls.py
from django.conf.urls.defaults import *
from mysite import views
urlpatterns = patterns('',
(r'^(foo)/$', views.foobar_view),
(r'^(bar)/$', views.foobar_view),
)
# views.py
from django.shortcuts import render_to_response
from mysite.models import MyModel
def foobar_view(request, url):
m_list = MyModel.objects.filter(is_new=True)
if url == 'foo':
template_name = 'template1.html'
elif url == 'bar':
template_name = 'template2.html'
return render_to_response(template_name, {'m_list': m_list})
Проблема такого решения заключается в том, что URL переносятся в код. Если вы решите переименовать foo в fooey, вам потребуется не забыть внести изменения в код функции представления. Элегантное решение требует включить необязательный параметр в шаблон схемы URL. Каждый шаблон в схеме может включать в себя третий элемент: словарь именованных аргументов для передачи его в функцию представления. Учитывая это, мы можем переписать наш предыдущий пример так: # urls.py
from django.conf.urls.defaults import *
from mysite import views
urlpatterns = patterns('',
(r'^foo/$', views.foobar_view, {'template_name': 'template1.html'}),
(r'^bar/$', views.foobar_view, {'template_name': 'template2.html'}),
)
# views.py
from django.shortcuts import render_to_response
from mysite.models import MyModel
def foobar_view(request, template_name):
m_list = MyModel.objects.filter(is_new=True)
return render_to_response(template_name, {'m_list': m_list})
Как можно увидеть, шаблон в схеме URL имеет третий параметр. Такая методика является отличным способом передачи дополнительной информации в функции представления с минимальными затратами. Эта же методика используется в поставляемых с Django приложениях, в основном в его основной системе представлений, которую мы рассмотрим в главе «Базовые представления». Следующие секции содержат ряд идей о том как вы можете использовать эту методику в своих проектах. Допустим, у вас есть набор представлений, которые соответствуют шаблону, наряду с другим URL, который не совпадает с шаблоном, но логика его функции представления аналогична. В этом случае, вы можете «подделать» получение значений из URL с помощью дополнительной опции для обработки этого дополнительного URL той же функцией представления. Например, у вас может быть приложение, которое отображает некие данные для определённого дня, вот с такими URL: /mydata/jan/01/ /mydata/jan/02/ /mydata/jan/03/ # ... /mydata/dec/30/ /mydata/dec/31/
Довольно просто это обработать, используя синтаксис именованных групп: urlpatterns = patterns('',
(r'^mydata/(?P<month>\w{3})/(?P<day>\d\d)/$', views.my_view),
)
А сигнатура функции представления станет такой: def my_view(request, month, day):
# ....
Это прямолинейный подход, ничего подобного раньше вы не видели. Эта уловка пригодится когда понадобится добавить другой URL, который будет использовать my_view, но её URL не будет обрабатывать month и/или day. Например, вы можете добавить другой URL, /mydata/birthday/, который будет аналогичен /mydata/jan/06/. В этом случае использование дополнительного параметра даст вам преимущество: urlpatterns = patterns('',
(r'^mydata/birthday/$', views.my_view, {'month': 'jan', 'day': '06'}),
(r'^mydata/(?P<month>\w{3})/(?P<day>\d\d)/$', views.my_view),
)
Секрет в том, что не требуется вносить какие-либо изменения
в код функции представления. Функция должна
обрабатывать параметры
Снижение количества дублирующего кода является хорошим тоном разработки программного обеспечения. Например, эти функции, написанные на языке Python: def say_hello(person_name):
print 'Hello, %s' % person_name
def say_goodbye(person_name):
print 'Goodbye, %s' % person_name
мы можем упростить, выделив приветствие в параметр функции: def greet(person_name, greeting):
print '%s, %s' % (greeting, person_name)
Вы можете использовать эту философию применимо к представлениям Django, используя дополнительные параметры шаблонов схемы URL.
Учитывая это, вы можете начать создание высокоуровневых
абстракций для ваших представлений. Вместо того, чтобы
думать «Это представление отображает список объектов
Для примера рассмотрим следующий код: # urls.py
from django.conf.urls.defaults import *
from mysite import views
urlpatterns = patterns('',
(r'^events/$', views.event_list),
(r'^blog/entries/$', views.entry_list),
)
# views.py
from django.shortcuts import render_to_response
from mysite.models import Event, BlogEntry
def event_list(request):
obj_list = Event.objects.all()
return render_to_response('mysite/event_list.html', {'event_list': obj_list})
def entry_list(request):
obj_list = BlogEntry.objects.all()
return render_to_response('mysite/blogentry_list.html', {'entry_list': obj_list})
Эти два представления делают то же самое: они отображают список объектов. Таким образом, можно упростить тип объекта, который они отображают: # urls.py
from django.conf.urls.defaults import *
from mysite import models, views
urlpatterns = patterns('',
(r'^events/$', views.object_list, {'model': models.Event}),
(r'^blog/entries/$', views.object_list, {'model': models.BlogEntry}),
)
# views.py
from django.shortcuts import render_to_response
def object_list(request, model):
obj_list = model.objects.all()
template_name = 'mysite/%s_list.html' % model.__name__.lower()
return render_to_response(template_name, {'object_list': obj_list})
Внеся небольшие изменения, мы неожиданно получили образцовое представление, пригодное для многократного использования. Начиная с этого момента, как только нам понадобится представление, которое отображает набор объектов в виде списка, мы можем просто использовать представление object_list, вместо создания ещё одного представления. Теперь рассмотрим, что же мы сделали:
Так как сайты, хранящие свои данные в базах данных, используют одинаковые шаблоны, Django поставляется с набором «общие представления», который используется вышеописанную методику для сохранения вашего времени. Мы рассмотрим встроенные общие представления Django в следующей главе. При распространении приложения для Django может так случиться, что ваши пользователи пожелают внести некоторые изменения в конфигурацию приложения. В таком случае, хорошей идеей будет добавить перехватчики к вашим представлениям для любого параметра конфигурации, который может подвергнуться такому изменению. Для этого опять надо использовать дополнительные параметры шаблона схемы URL. Чаще всего изменению подвергаются имена шаблонов: def my_view(request, template_name):
var = do_something()
return render_to_response(template_name, {'var': var})
При наличии конфликта параметров, дополнительные параметры шаблона схемы URL получают преимущество над передаваемыми в функцию параметрами. Другими словами, если ваш шаблон получил переменную с именованной группой, а дополнительный параметр включает в себя переменную с таким же именем, именно он и будет использован. Например: from django.conf.urls.defaults import *
urlpatterns = patterns('',
(r'^mydata/(?P<id>\d+)/$', views.my_view, {'id': 3}),
)
В данном примере параметр
Проницательные читатели отметят, что в данном случае захват
переменной Другим удобным способом является указание стандартных значений для аргументов представления. Эти значения используются в случае, когда соответствующие параметры не определены при вызове функций. Пример: # urls.py
from django.conf.urls.defaults import *
urlpatterns = patterns('',
(r'^blog/$', views.page),
(r'^blog/page(?P<num>\d+)/$', views.page),
)
# views.py
def page(request, num="1"):
# Output the appropriate page of blog entries, according to num.
# ...
В данном примере оба шаблона URL указывают на одно и то же
представление, views.page, но первый шаблон не
получает параметров из URL. Если срабатывает первый шаблон, то
будет выполнена функция
Как объяснялось ранее, обычно используют данную методику
совместно с параметрами конфигурации. Этот пример немного
улучшает пример из раздела «Giving a View Configuration Options»,
предоставляя стандартное значение для
def my_view(request, template_name='mysite/my_view.html'):
var = do_something()
return render_to_response(template_name, {'var': var})
Иногда так случается, что есть шаблон в вашей схеме URL, который обрабатывает большой набор URL, но вам необходимо реализовать для них особые случаи. Для этого надо просто описать эти случаи первыми. Например, страницы «добавить объект» на сайте администратора Django представлены в схеме URL такой строкой: urlpatterns = patterns('',
# ...
('^([^/]+)/([^/]+)/add/$', 'django.contrib.admin.views.main.add_stage'),
# ...
)
Эта строка совпадает с URL такими как /myblog/entries/add/ и /auth/groups/add/. Однако страница добавления пользователей (/auth/user/add/) является особым случаем — оно не отображает все поля формы, она отображает два поля для ввода пароля и так далее. Мы можем решить эту проблему реализовав особое поведение представления, например так: def add_stage(request, app_label, model_name):
if app_label == 'auth' and model_name == 'user':
# do special-case code
else:
# do normal code
Но такой подход не элегантен по причинам, которые мы обсуждали чуть ранее: этот подход помещает логику обработки URL в представление. В качестве элегантного решения мы можем воспользоваться тем фактом, что шаблоны URL обрабатываются в порядке их определения в файле: urlpatterns = patterns('',
# ...
('^auth/user/add/$', 'django.contrib.admin.views.auth.user_add_stage'),
('^([^/]+)/([^/]+)/add/$', 'django.contrib.admin.views.main.add_stage'),
# ...
)
Теперь вызов /auth/user/add/ будет обработан представлением user_add_stage. Несмотря на то, что этот URL совпадает со вторым шаблоном, он будет обработан первым. Каждый аргумент, полученный из URL с помощью регулярного выражения передаётся представлению в виде обычной строки. Например: (r'^articles/(?P<year>\d{4})/$', views.year_archive),
Аргумент
Эту особенность следует помнить при разработке
приложения. Множество встроенных функций Python принимают
объекты строго определённого типа. Общей ошибкой является
попытка создать объект >>> import datetime
>>> datetime.date('1993', '7', '9')
Traceback (most recent call last):
...
TypeError: an integer is required
>>> datetime.date(1993, 7, 9)
datetime.date(1993, 7, 9)
С точки зрения схемы URL это ошибка выглядит так: # urls.py
from django.conf.urls.defaults import *
urlpatterns = patterns('',
(r'^articles/(\d{4})/(\d{2})/(\d{2})/$', views.day_archive),
)
# views.py
import datetime
def day_archive(request, year, month, day)
# The following statement raises a TypeError!
date = datetime.date(year, month, day)
А правильно можно написать так: def day_archive(request, year, month, day)
date = datetime.date(int(year), int(month), int(day))
Следует отметить, что сам метод При получении запроса Django пытается найти соответствующий шаблон в схеме URL как для обычной строки Python (не как для Unicode строки). При этом параметры GET и POST или доменное имя не принимаются во внимание. Также не рассматривается начальный слэш, потому что каждое URL имеет начальный слэш. Например, в запросе http://www.example.com/myapp/ Django будет искать шаблон URL для myapp/. В запросе http://www.example.com/myapp/?page=3 Django будет искать шаблон для myapp/. Метод запроса (т.е. GET, POST, HEAD) не принимается во внимание при обработке схемы URL. Другими словами, все методы будут обрабатываться теми же функциями. Ветвление обработки запроса в зависимости от использованного метода лежит полностью на функциях представления. |
Увидели ошибку?
Выделите её мышкой и нажмите
-
Обработано:
511
49
130
71
Версия книги
1.0
2.0
Версия 2.0 в процессе перевода!
Мой луч
Многообразие света
Полезное
Актуальные вакансии,
Python работа
для python-разработчиков.
Скачать в формате
CHM от 2 сентябряЗаказать PDF файл можно через почту, чат, джаббер. Всего 2WMZ. Содержимое точно соответствует содержимому сайта. Чем чаще заказываете — тем больше перевожу. Русская группа
Ускорить процесс перевода!
R130494980980
Z425285133788 E112528079659 U327380922061 Книга помогла реализовать:
|
| © 2008-2009 Ruslan Popov @ gmail.com | Powered by Django 1.1 beta 1 SVN-11114 |