Написание вашего первого приложения 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. Эти взгляды немного отличаются, потому что они аргументируют:

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()вызовы:

polls / urls.py
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 вопросов опроса в системе, разделенных запятыми, в соответствии с датой публикации:

polls / views.py
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 на правильный путь, и лучший способ гарантировать это - разместить их в пространстве имен . То есть, поместив эти шаблоны в другой каталог, названный по имени самого приложения.

Поместите в этот шаблон следующий код:

опросы / шаблоны / опросы / 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 %}

Примечание

Чтобы сделать руководство короче, все примеры шаблонов используют неполный HTML. В своих проектах вы должны использовать полные HTML-документы .

Теперь давайте обновим наше indexпредставление, polls/views.pyчтобы использовать шаблон:

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()вид, переписанный:

polls / views.py
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

Теперь давайте займемся представлением деталей вопроса - страницей, на которой отображается текст вопроса для данного опроса. Вот вид:

polls / views.py
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шаблон немного позже, но если вы хотите быстро заставить приведенный выше пример работать, файл, содержащий только:

опросы / шаблоны / опросы / detail.html
{{ question }}

поможет вам начать сейчас.

Ярлык: get_object_or_404()

Это очень распространенная идиома, которую можно использовать get() и вызывать, Http404если объект не существует. Django предоставляет ярлык. Вот detail()вид в переписанном виде:

polls / views.py
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может выглядеть шаблон:

опросы / шаблоны / опросы / 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.allquestion.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чтобы установить пространство имен приложения:

polls / urls.py
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шаблон с:

опросы / шаблоны / опросы / index.html
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>

чтобы указать на подробное представление с пространством имен:

опросы / шаблоны / опросы / index.html
<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>

Когда вы научитесь писать представления, прочитайте часть 4 этого руководства, чтобы узнать основы обработки форм и общих представлений.

Copyright ©2021 All rights reserved