Содержание
Введение
Представление (view) – это объект-страница в вашем Django-приложении, которое имеет специальные функции и специальный шаблон. Например, в приложении-блоге у вас могут быть такие представления:
- домашняя страница блога – отображает последние записи ленты;
- страница отдельной записи – постоянная страница для одной записи;
- архивы записей по годам – отображает все месяцы, в которых были сделаны записи в заданном году;
- архивы записей по месяцам – отображает все дни, в которых были сделаны записи в заданном месяце;
- архивы записей по дням – отображает все записи за указанный день;
- система комментариев – обрабатывает комментирование отдельной записи.
В нашем приложении-голосовании у нас будет 4 представления:
- главная страница вопросов – отображение нескольких последних вопросов;
- детали вопроса – отображение текста вопроса, без результатов голосования но с формой добавления голоса;
- результаты голосования по вопросу – отображение результатов по каждому конкретному вопросу;
- голосование – обработка голосования по каждому варианту ответа для вопроса.
В Django страницы и другой контент создаются представлениями. Каждое представление является простой функцией Python (или методом, в случае представлений, основанных на классах ООП). Django выбирает представление, основываясь на URL-е обращения (если точнее – то на той части URL-а, которая следует за доменным именем).
Вы, вероятно, уже встречались с такими “прелестными” URL-ами как “ME2/Sites/dirmod.asp?sid=&type=gen&mod=Core+Pages&gid=A6CD4967199A42D9B65B1B” – и будете приятно удивлены, что Django предлагает намного более элегантный вид URL-ов для вашего проекта.
Паттерн URL-а – это часть запроса, следующая за доменом, например – /newsarchive/<year>/<month>/.
Что бы передать URL представлению – Django использует то, что мы называем URLconf
. URLconf
связывает паттерны URL-ов с представлениями.
В этой части мы рассмотрим основы работы с URLconf
, а более полную информацию вы можете получить на странице django.core.urlresolvers
.
Пишем ваше первое представление
Давайте напишем первое представление. Откройте файл polls/views.py
и впишите туда такой код:
from django.http import HttpResponse def index(request): return HttpResponse("Hello, world. You're at the polls index.")
Это самое простое представление в Django. Что бы вызвать его – нам надо связать его с URL-ом – и для этого нам потребуется URLconf
.
Что бы создать URLconf
– создайте файл polls/urls.py
. Теперь ваш каталог с приложением должен теперь выглядеть так:
$ tree -L 2 polls/ polls/ ├── admin.py ├── admin.pyc ├── __init__.py ├── __init__.pyc ├── migrations │ ├── 0001_initial.py │ ├── 0001_initial.pyc │ ├── __init__.py │ └── __init__.pyc ├── models.py ├── models.pyc ├── tests.py ├── urls.py └── views.py
В файл polls/urls.py
впишите следующий код:
from django.conf.urls import url from . import views urlpatterns = [ url(r'^$', views.index, name='index'), ]
Следующий шаг – связать URLconf
всего проекта с модулем polls.urls
, который мы только что создали. В файле mysite/urls.py
добавьте импорт и вызов include()
:
from django.conf.urls import include, url from django.contrib import admin urlpatterns = [ url(r'^polls/', include('polls.urls')), url(r'^admin/', include(admin.site.urls)), ]
Теперь у вас есть представление index()
, связанное с вашим URLconf
.
Запустите ваш сервер разработки, если он ещё не запущен:
$ python manage.py runserver 77.***.***.20:8000
И перейдите на страницу http://ваш_IP:8000/polls – вы должны увидеть текст “Hello, world. You’re at the polls index”, который мы вписали в представление index()
.
Функции url()
передаётся четыре аргумента: два обязательных – regex
и view
, и два опциональных – kwargs
и name
. Давайте рассмотрим их.
url()
аргумент: regex
Термин regex – сокращение от regular expression (регулярное выражение), которое предоставляет специальный синтаксис для получения совпадения по шаблону из строки, или, в нашем случае, из URL. Django начинает проверку с первого выражения в списке, и продолжает проверку всех выражений по очереди вниз, сравнивая переданный URL с каждым выражением, пока не найдёт совпадение.
Заметьте, что регулярные выражения в URLconf
не выполняет поиск в GET
или POST
запросах, как и в самом доменном имени. Например, в запросе http://www.example.com/myapp/ URLconf
будет выполнять поиск только по myapp/. В запросе http://www.example.com/myapp/?page=3 – аналогично, поиск будет выполняться только по myapp/.
Если вам нужна помощь в регулярных выражениях – вы можете получить её на странице в Википедии, в документации по модулю Python re, или в таблице RegEx: полная таблица.
url()
аргумент: view
Когда Django находит совпадение в URL с заданным регулярным выражением – вызывается указанная функция представления, передавая ей первым аргументом объект HttpRequest
, и “захваченные” значения из регулярного выражения другими аргументами. Если regex
использует простой захват – значения передаются как позиционные аргументы; если используется именованный захват – значения передаются в виде аргументов с ключевыми словами.
url()
аргумент: kwargs
Произвольные аргументы в виде ключевых слов могут быть переданы в виде словаря целевому представлению. Мы не будем использовать этот подход в нашем руководстве.
url()
аргумент: name
Создание имени для ваших URL-ов позволяет обращаться к ним во всем проекте (или приложении), что может быть особенно удобно в шаблонах. Это очень полезная возможность, которая позволяет вам производить глобальные изменения в шаблонах URL-ов, редактируя только один файл. Мы рассмотри её ниже в этой статье.
Пишем больше представлений
Теперь – давайте добавим ещё несколько представлений в наш polls/views.py
. Они немного отличаются от первого, так как будут принимать аргументы:
def detail(request, question_id): return HttpResponse("You're looking at question %s." % question_id) def results(request, question_id): response = "You're looking at the results of question %s." return HttpResponse(response % question_id) def vote(request, question_id): return HttpResponse("You're voting on question %s." % question_id)
Свяжите эти представления с вашим модулем polls.urls
, добавив такие вызовы url()
:
from django.conf.urls import url from . import views urlpatterns = [ # ex: /polls/ url(r'^$', views.index, name='index'), # ex: /polls/5/ url(r'^(?P<question_id>[0-9]+)/$', views.detail, name='detail'), # ex: /polls/5/results/ url(r'^(?P<question_id>[0-9]+)/results/$', views.results, name='results'), # ex: /polls/5/vote/ url(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote'), ]
Откройте в браузере страницу /polls/34/. Она вызовет функцию detail()
и отобразит ID, который вы передали в URL. Попробуйте так же открыть /polls/34/results/ и /polls/34/vote/.
Когда кто-то запрашивает страницу на вашем сайте, например – /polls/34/, Django загружает модуль mysite.urls
, так как он указан в параметре ROOT_URLCONF
. Там находится переменная urlpatterns
, в которой по очереди проверяются регулярные выражения. Функция include()
используется для создания ссылки на другой URLconf
. Заметьте, что регулярное выражение для include()
не имеет закрывающего символа $
(символ конца строки), а вместо этого – используется символ косой черты. Каждый раз, когда Django встречает include()
– он “обрезает” URL до точки совпадения с выражением, передаёт оставшуюся часть подключенному URLconf
-у для дальнейшей обработки.
Основная идея в использовании include()
– это возможность простого подключения новых URL-ов. Так как голосование имеет собственный URLconf
(файл polls/urls.py
) – её URL-ы могут быть расположены “под” /polls/, или /fun_polls/, или /content/polls/, или в любом другом месте – и приложение будет работать.
Вот что происходит, когда пользователь обращается к адресу /polls/34/ в нашей системе:
- Django находит совпадение в
'^polls/'
; - затем, Django обрезает текст, попавший под шаблон (polls/), и отправляет оставшуюся строку – “34/” – к
URLconf
-уpolls.urls
для дальнейшей обработки, где остаток строки строки попадает под шаблонr'^(?P<question_id>[0-9]+)/$'
и вызывает представлениеdetail()
так:detail(request=<HttpRequest object>, question_id='34')
Часть question_id=’34’ появляется из захвата (?P<question_id>[0-9]+)
. Используя скобки вокруг шаблона – вы можете “захватывать” текст, попавший под шаблон и передать его аргументом представлению. ?P<question_id>
определяет имя, которое будет использоваться для захваченного текста, а [0-9]+
– регулярное выражение для получения набора цифр.
Пишем представление, которое выполняет реальные действия
Каждое представление ответственно за одну из двух задач – либо возвращает объект HttpResponse
, содержащий контент для запрошенной страницы, либо – вызывает исключение, такое как Http404
. Всё остальное – на ваше усмотрение.
Ваше представление может считывать записи из базы – или нет. Оно может использовать систему шаблонов – родную, Django, или систему сторонних разработчиков – или нет. Оно может генерировать PDF-файл, XML, создавать ZIP-архив – что угодно, используя все доступные библиотеки Python.
Всё, чего хочет Django – это объект HttpResponse
. Или исключение.
В нашем случае – мы будем использовать Django API для работы с базой данных, который мы рассматривали в предыдущей части. Вот как мы можем изменить наше представление index()
, что бы отобразить 5 последних вопросов в нашей базе, разделённых запятой, отсортированных по дате публикации:
from django.http import HttpResponse from .models import Question def index(request): latest_question_list = Question.objects.order_by('-pub_date')[:5] output = ', '.join([p.question_text for p in latest_question_list]) return HttpResponse(output) ...
Остальную часть оставьте без изменений.
Однако – тут имеется проблема: дизайн страницы жестко прописан прямо в коде представления. Если вы захотите изменить внешний вид страницы – вам придётся редактировать код Python. Давайте используем систему шаблонов Django, что бы отделить дизайн страницы от представления, и создадим шаблон, который будет использоваться представлением.
Для начал – создайте директорию templates
в каталоге polls
– Django будет искать шаблоны именно в ней.
Параметр TEMPLATES
в файле settings.py
указывает Django как загружать и рендерить шаблоны. По умолчанию в Django активирован модуль DjangoTemplates
, параметр APP_DIRS
которого установлен в True
, что указывает Django выполнять поиск каталога templates
в каталоге каждого приложения из списка INSTALLED_APPS
. Вот как Django знает, что шаблоны приложения polls
надо искать каталоге templates
, хотя мы не меняли настройки DIRS
, как мы делали это в третьей части.
Внутри каталога templates
, который вы только что создали, создайте ещё один каталог – polls
, а в нём создайте файл index.html
. Другими словами – ваш шаблон должен находиться в файле polls/templates/polls/index.html
. Учитывая рассмотренное выше – вы можете обращаться к нему как polls/index.html
.
Пространство имён в шаблонах
Вы можете располагать ваши шаблоны прямо в каталоге polls/templates
, а не создавать ещё одну вложенную директорию – однако это может быть очень плохой идеей. Django выбирает первый совпавший по имени шаблон, и если у вас будет шаблон с тем же именем, но для другого приложения – Django не сможет их различить. Нам необходимо точно указать Django используемый файл шаблона, и самый простой путь к этому – разделить их по разным пространствам имён, т.е. – расположив их в разных каталогах.
Впишите в файл шаблона polls/templates/polls/index.html
такой код:
{% if latest_question_list %} <ul> {% for question in latest_question_list %} <li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li> {% endfor %} </ul> {% else %} <p>No polls are available.</p> {% endif %}
Теперь – давайте обновим наше представление index()
в файле polls/views.py
, что бы его использовать:
from django.http import HttpResponse from django.template import RequestContext, loader from .models import Question def index(request): latest_question_list = Question.objects.order_by('-pub_date')[:5] template = loader.get_template('polls/index.html') context = RequestContext(request, { 'latest_question_list': latest_question_list, }) return HttpResponse(template.render(context)) ...
Этот код загружает шаблон polls/index.html
и передаёт ему контекст. Контекст – это словарь, связывающий переменные в шаблоне с объектами Python.
Обновите страницу /polls/ в браузере, и вы должны увидеть маркированный список вопросов, в котором будет вопрос “What’s up?” из второй части этого руководства. Ссылка направлена на страницу деталей этого вопроса.
функция render()
Задача по загрузке шаблона, наполнению контекста и возврату HttpResponse
– очень частая. Поэтому, Django предоставляет в использование функцию render()
. Вот переписанное представление index()
с её использованием:
from django.shortcuts import render from .models import Question def index(request): latest_question_list = Question.objects.order_by('-pub_date')[:5] context = {'latest_question_list': latest_question_list} return render(request, 'polls/index.html', context) ...
Помните, что если переписать все три функции на использование render()
– то нам больше не потребуется выполнять импорт loader
, RequestContext
и HttpResponse
.
Функция render()
принимает первым аргументом объект запроса, имя шаблона – вторым аргументом и словарь – опциональным третьим аргументом, а возвращает объект HttpResponse
из шаблона с контекстом.
Создание ошибки 404
Теперь – давайте приведём в порядок функцию detail()
– страницу, которая отображает детали выбранного вопроса:
from django.http import Http404 from django.shortcuts import render from .models import Question # ... def detail(request, question_id): try: question = Question.objects.get(pk=question_id) except Question.DoesNotExist: raise Http404("Question does not exist") return render(request, 'polls/detail.html', {'question': question}) ...
Тут мы используем новое свойство – представление вызывает исключение Http404
, если запрошенный ID не существует.
Мы рассмотрим подробнее содержимое страницы polls/detail.html
позже, но если вы хотите посмотреть результат прямо сейчас – просто добавьте в файл polls/templates/polls/detail.html
такую строку:
{{ question }}
Функция get_object_or_404()
Очень часто функции get()
и Http404
используются вместе, что бы вызвать 404, если объект не найден. Поэтому – Django предлагает сокращение для таких случаев.
Вот переписанная функция detail()
:
... from django.shortcuts import get_object_or_404, render from .models import Question # ... def detail(request, question_id): question = get_object_or_404(Question, pk=question_id) return render(request, 'polls/detail.html', {'question': question}) ...
Функция get_object_or_404()
принимает первым аргументом модель Django, и набор ключевых слов остальными аргументами, которые передаются функции менеджера модели get()
. В случае, если объект не найден – вызывается исключение Http404
.
Так же – существует функция get_list_or_404()
, которая работает аналогично get_object_or_404()
, за исключением того, что она использует функцию filter()
, а не get(
). В случае, если список пустой – вызывается Http404
.
Использование системы шаблонов
Возвращаясь к представлению detail()
нашего приложения polls
. Используя переданную переменную question
– вот как мы можем использовать её в файле шаблона polls/detail.html
:
<h1>{{ question.question_text }}</h1> <ul> {% for choice in question.choice_set.all %} <li>{{ choice.choice_text }}</li> {% endfor %} </ul>
Система шаблонов использует синтаксис с разделением точкой для доступа к атрибутам переменной. В нашем примере с {{ question.question_text }}
– сначала Django выполняет поиск по объекту question
как по словарю. Если это не удаётся – выполняется поиск атрибутов – который, в данном случае, сработает. Если и такой поиск будет безуспешным – будет выполнен поиск по словарю.
Вызов по методу выполняется в цикле {% for %}
: {% for %} question.choice_set.all
интерпретируется как Python вызов question.choice_set.all()
, который возвращает объект Choice
, над которым можно выполнить итерацию и который допустим к использованию в >{% for %}.
Больше информации о шаблонах можно найти в template guide или в цикле Django Book: основы системы шаблонов.
Удаляем URL-ы из шаблонов
Помните, когда мы вписывали ссылку на вопрос к файле шаблона polls/index.html
– она в нём была жестко прописана:
<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
Проблема в таком подходе заключается в том, что замена URL-а при использовании множества файлов шаблонов становится очень сложной задачей. Однако, так как вы объявили аргумент name в функции url()
в модуле polls.urls
– вы можете удалить эту ссылку, и переписать её с использованием тега {% url %}
в шаблоне:
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
Таким образом – выполняется поиск указанного URL в модуле polls.urls
, который в нашем случае выглядит так:
... # ex: /polls/5/ url(r'^(?P<question_id>[0-9]+)/$', views.detail, name='detail'), ...
Если вы захотите изменить URL для деталей вопроса, например на polls/specifics/12
, вместо того, что бы менять его во всех файлов шаблонов, где он используется, вам достаточно будет изменить его в polls/urls.py
:
... # added the word 'specifics' url(r'^specifics/(?P<question_id>[0-9]+)/$', views.detail, name='detail'), ...
Пространства имён в URL-ах
Наш проект имеет только одно приложение – polls
. В реальных проектах Django – у вас может быть и пять, и двадцать пять приложений. Как Django различает имена URL-ов между ними? Например, приложение polls
содержит представление detail(
), а так же, допустим, другое приложение – блог, в этом же проекте. Как сделать так, что бы Django знал – чьё представление использовать для адреса, указанного в теге {% url %}
?
Ответ – в использовании пространства имён в вашем URLconf
-е. В файле mysite/urls.py
внесите такие изменения, что бы определить пространство имён:
from django.conf.urls import include, url from django.contrib import admin urlpatterns = [ url(r'^polls/', include('polls.urls', namespace="polls")), url(r'^admin/', include(admin.site.urls)), ]
Теперь – измените шаблон polls/index.html
с:
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
на:
<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>
Готово.
Продолжение – Django: пример создания приложения – часть 5: создание форм и общие представления (generic views)