Написание вашего первого приложения Django, часть 3 ¶
Этот учебник начинается с того места, где остановился Урок 2 . Мы продолжаем работу над приложением веб-опроса и сосредоточимся на создании общедоступного интерфейса - «просмотров».
Где получить помощь:
Если у вас возникли проблемы с прохождением этого руководства, перейдите в раздел « Получение справки » в FAQ.
Обзор ¶
Представление - это «тип» веб-страницы в вашем приложении Django, который обычно выполняет определенную функцию и имеет определенный шаблон. Например, в приложении блога у вас могут быть следующие представления:
- Домашняя страница блога - отображает несколько последних записей.
- Страница «подробностей» записи - страница с постоянной ссылкой для одной записи.
- Страница архива по годам - отображает все месяцы с записями в данном году.
- Страница архива по месяцам - отображает все дни с записями в данном месяце.
- Страница архива по дням - отображает все записи за данный день.
- Действие комментария - обрабатывает публикацию комментариев к данной записи.
В нашем приложении для опроса у нас будет четыре представления:
- Страница «Указатель» вопросов - отображает несколько последних вопросов.
- Страница «Детали» вопроса - отображает текст вопроса без результатов, но с формой для голосования.
- Страница «Результаты» вопроса - отображает результаты по конкретному вопросу.
- Действие голосования - обрабатывает голосование за определенный вариант в конкретном вопросе.
В Django веб-страницы и другой контент доставляются с помощью представлений. Каждое представление представлено функцией Python (или методом в случае представлений на основе классов). Django выберет представление, проверив запрошенный URL-адрес (точнее, часть URL-адреса после имени домена).
Сейчас, когда вы находитесь в сети, вы, возможно, встречали таких красоток, как
ME2/Sites/dirmod.htm?sid=&type=gen&mod=Core+Pages&gid=A6CD4967199A42D9B65B1B
. Вам будет приятно узнать, что Django позволяет нам гораздо более элегантные
шаблоны URL-адресов, чем это.
Шаблон URL - адреса является общей формой URL - например:
/newsarchive/<year>/<month>/
.
Чтобы перейти от URL-адреса к представлению, Django использует так называемые «URLconfs». URLconf сопоставляет шаблоны URL-адресов с представлениями.
В этом руководстве представлены базовые инструкции по использованию URLconfs, и вы можете обратиться к диспетчеру 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
модулю, добавив следующие
path()
вызовы:
from django.urls import path
from . import views
urlpatterns = [
# ex: /polls/
path('', views.index, name='index'),
# ex: /polls/5/
path('<int:question_id>/', views.detail, name='detail'),
# ex: /polls/5/results/
path('<int:question_id>/results/', views.results, name='results'),
# ex: /polls/5/vote/
path('<int:question_id>/vote/', views.vote, name='vote'),
]
Взгляните в своем браузере на «/ polls / 34 /». Он запустит detail()
метод и отобразит любой идентификатор, который вы укажете в URL-адресе. Попробуйте также «/ polls / 34 / results /» и «/ polls / 34 / vote /» - они будут отображать результаты заполнителей и страницы голосования.
Когда кто-то запрашивает страницу с вашего веб-сайта - скажем, «/ polls / 34 /», Django загружает mysite.urls
модуль Python, потому что на него указывает
ROOT_URLCONF
настройка. Он находит указанную переменную urlpatterns
и просматривает шаблоны по порядку. После нахождения совпадения в 'polls/'
, он удаляет совпадающий текст ( "polls/"
) и отправляет оставшийся текст -
"34/"
- в URLconf 'polls.urls' для дальнейшей обработки. Там он совпадает '<int:question_id>/'
, что приводит к вызову такого detail()
вида:
detail(request=<HttpRequest object>, question_id=34)
question_id=34
Часть поступает из <int:question_id>
. Использование угловых скобок «захватывает» часть URL-адреса и отправляет ее в качестве аргумента ключевого слова функции просмотра. :question_id>
Часть строки определяет имя , которое будет использоваться для идентификации подходящего шаблона, а <int:
часть представляет собой преобразователь , который определяет , какие модели должны соответствовать этой части URL пути.
Пишите представления, которые действительно что-то делают ¶
Каждое представление отвечает за выполнение одного из двух действий: возвращает
HttpResponse
объект, содержащий контент для запрошенной страницы, или вызывает исключение, например Http404
. Остальное зависит от тебя.
Ваше представление может читать записи из базы данных или нет. Он может использовать систему шаблонов, такую как Django, или стороннюю систему шаблонов Python, или нет. Он может генерировать PDF-файл, выводить XML, создавать ZIP-файл на лету, все, что вы хотите, используя любые библиотеки Python, которые вы хотите.
Все, чего хочет Django, - это этого HttpResponse
. Или исключение.
Поскольку это удобно, давайте воспользуемся собственным API базы данных Django, который мы рассмотрели в Уроке 2 . Вот один удар по новому 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([q.question_text for q in latest_question_list])
return HttpResponse(output)
# Leave the rest of the views (detail, results, vote) unchanged
Однако здесь есть проблема: дизайн страницы жестко запрограммирован в представлении. Если вы хотите изменить внешний вид страницы, вам придется отредактировать этот код Python. Итак, давайте воспользуемся системой шаблонов Django, чтобы отделить дизайн от Python, создав шаблон, который может использовать представление.
Сначала создайте каталог с именем templates
в вашем polls
каталоге. Django будет искать там шаблоны.
TEMPLATES
Настройки вашего проекта описывают, как Django будет загружать и отображать шаблоны. Файл настроек по умолчанию настраивает DjangoTemplates
серверную часть, для которой APP_DIRS
установлено значение
True
. По соглашению DjangoTemplates
ищет подкаталог «шаблоны» в каждом из файлов INSTALLED_APPS
.
В templates
только что созданном каталоге создайте еще один каталог с именем polls
и внутри него файл с именем
index.html
. Другими словами, ваш шаблон должен быть по адресу
polls/templates/polls/index.html
. Поскольку app_directories
загрузчик шаблонов работает, как описано выше, вы можете ссылаться на этот шаблон в Django как polls/index.html
.
Пространство имён шаблона
Теперь нам, возможно, удастся разместить наши шаблоны напрямую
polls/templates
(вместо того, чтобы создавать другой polls
подкаталог), но на самом деле это было бы плохой идеей. Django выберет первый найденный шаблон, имя которого совпадает, и если бы у вас был шаблон с таким же именем в другом приложении, Django не смог бы их различить. Нам нужно указать Django на правильный путь, и лучший способ гарантировать это - разместить их в пространстве имен . То есть, поместив эти шаблоны в другой каталог, названный по имени самого приложения.
Поместите в этот шаблон следующий код:
{% 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 %}
Примечание
Чтобы сделать руководство короче, все примеры шаблонов используют неполный HTML. В своих проектах вы должны использовать полные HTML-документы .
Теперь давайте обновим наше index
представление, polls/views.py
чтобы использовать шаблон:
from django.http import HttpResponse
from django.template import 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 = {
'latest_question_list': latest_question_list,
}
return HttpResponse(template.render(context, request))
Этот код загружает вызываемый шаблон polls/index.html
и передает ему контекст. Контекст - это словарь, сопоставляющий имена переменных шаблона с объектами Python.
Загрузите страницу, указав в браузере «/ polls /», и вы должны увидеть маркированный список, содержащий вопрос «Что случилось?» Из Урока 2 . Ссылка указывает на страницу с подробными сведениями о вопросе.
Ярлык: ¶render()
Это очень распространенная идиома для загрузки шаблона, заполнения контекста и возврата
HttpResponse
объекта с результатом визуализированного шаблона. Django предоставляет ярлык. Вот полный 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)
Обратите внимание , что , как только мы сделали это во всех этих взглядах, мы больше не нужно импортировать
loader
и HttpResponse
(вы хотите сохранить , HttpResponse
если вы до сих пор методы заглушек для detail
,
results
и vote
).
render()
Функция принимает объект запроса в качестве первого аргумента, имени шаблона в качестве второго аргумента и словаря в качестве его необязательного третьего аргумента. Он возвращает HttpResponse
объект данного шаблона, обработанный с заданным контекстом.
Вызывает ошибку 404 ¶
Теперь давайте займемся представлением деталей вопроса - страницей, на которой отображается текст вопроса для данного опроса. Вот вид:
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
исключение, если вопрос с запрошенным идентификатором не существует.
Мы обсудим, что вы могли бы добавить в этот polls/detail.html
шаблон немного позже, но если вы хотите быстро заставить приведенный выше пример работать, файл, содержащий только:
{{ question }}
поможет вам начать сейчас.
Ярлык: ¶get_object_or_404()
Это очень распространенная идиома, которую можно использовать get()
и вызывать, Http404
если объект не существует. 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_object_or_404()
вместо автоматического перехвата
ObjectDoesNotExist
исключений на более высоком уровне или Http404
вместо того, чтобы
повышать API модели ObjectDoesNotExist
?
Потому что это свяжет слой модели со слоем вида. Одна из основных целей дизайна Django - поддерживать слабую связь. В django.shortcuts
модуль введена некоторая управляемая связь .
Также есть get_list_or_404()
функция, которая работает точно так же get_object_or_404()
- за исключением использования
filter()
вместо
get()
. Возникает,
Http404
если список пуст.
Используйте систему шаблонов ¶
Вернемся к detail()
представлению нашего приложения для опроса. Учитывая переменную контекста question
, вот как polls/detail.html
может выглядеть шаблон:
<h1>{{ question.question_text }}</h1>
<ul>
{% for choice in question.choice_set.all %}
<li>{{ choice.choice_text }}</li>
{% endfor %}
</ul>
Система шаблонов использует синтаксис поиска по точкам для доступа к атрибутам переменных. В примере , сначала Django выполняет поиск объекта в словаре . В противном случае он пытается выполнить поиск по атрибуту, который в данном случае работает. Если поиск атрибута завершился неудачно, он попытался бы выполнить поиск по индексу списка.{{ question.question_text }}
question
Вызов метода происходит в цикле:
интерпретируется как код Python
, который возвращает итерацию объектов и подходит для использования в теге.{% for %}
question.choice_set.all
question.choice_set.all()
Choice
{% for %}
См. Руководство по шаблонам для получения дополнительной информации о шаблонах.
Удаление жестко заданных URL-адресов в шаблонах ¶
Помните, когда мы писали ссылку на вопрос в polls/index.html
шаблоне, ссылка была частично жестко закодирована следующим образом:
<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
Проблема с этим жестко запрограммированным, тесно связанным подходом заключается в том, что становится сложно изменить URL-адреса в проектах с большим количеством шаблонов. Однако, поскольку вы определили аргумент имени в path()
функциях в polls.urls
модуле, вы можете удалить зависимость от определенных путей URL, определенных в ваших конфигурациях URL, с помощью тега шаблона:{% url %}
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
Это работает путем поиска определения URL-адреса, указанного в
polls.urls
модуле. Вы можете увидеть, где именно определено URL-имя "detail" ниже:
...
# the 'name' value as called by the {% url %} template tag
path('<int:question_id>/', views.detail, name='detail'),
...
Если вы хотите изменить URL-адрес подробного представления опросов на что-то другое, возможно, на что-то вроде того, polls/specifics/12/
вместо того, чтобы делать это в шаблоне (или шаблонах), вы бы изменили его в polls/urls.py
:
...
# added the word 'specifics'
path('specifics/<int:question_id>/', views.detail, name='detail'),
...
Имена URL пространств имен ¶
Учебник проект имеет только одно приложение, polls
. В реальных проектах Django может быть пять, десять, двадцать приложений или больше. Как Django различает имена URL между ними? Например, у polls
приложения есть detail
представление, как и у приложения в том же проекте, что и для блога. Как сделать так, чтобы Django знал, какое представление приложения нужно создать для URL-адреса при использовании
тега шаблона?{% url %}
Ответ - добавить пространства имен в ваш URLconf. В polls/urls.py
файле добавьте, app_name
чтобы установить пространство имен приложения:
from django.urls import path
from . import views
app_name = 'polls'
urlpatterns = [
path('', views.index, name='index'),
path('<int:question_id>/', views.detail, name='detail'),
path('<int:question_id>/results/', views.results, name='results'),
path('<int:question_id>/vote/', views.vote, name='vote'),
]
Теперь измените свой 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>
Когда вы научитесь писать представления, прочитайте часть 4 этого руководства, чтобы узнать основы обработки форм и общих представлений.