Написание вашего первого приложения 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-адреса; напр /archive/<année>/<mois>/ .

Чтобы переключиться с URL на просмотр, Django использует так называемые конфигурации URL (URLconf). Конфигурация URL связывает шаблоны URL с представлениями.

В этом руководстве представлены базовые инструкции по использованию конфигураций URL-адресов, а дополнительные сведения можно найти в разделе « Распределение 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)

Свяжите эти новые представления с их URL-адресами в модуле 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 загружает модуль Python, mysite.urls потому что он упоминается в настройке ROOT_URLCONF . Он находит названную переменную urlpatterns и циклически перебирает шаблоны по порядку. Найдя совпадение 'polls/' , он удаляет совпадающий текст ( "polls/" ) и передает оставшийся текст - "34/" - в конфигурацию URL «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, или стороннюю систему шаблонов, или нет. Он может генерировать 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 ищите подкаталог "templates" в каждом приложении, перечисленном в INSTALLED_APPS .

В templates только что созданном каталоге создайте другой каталог с именем, polls в который вы поместите новый файл index.html . Другими словами, путь к вашему шаблону должен быть polls/templates/polls/index.html . В зависимости от того, как работает загрузчик шаблонов app_directories (см. Объяснение выше), вы можете ссылаться на этот шаблон в Django как polls/index.html .

Пространство имен шаблона

Также можно было бы помещать наши шаблоны непосредственно в polls/templates (а не в подкаталог polls ), но это было бы плохой идеей. 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 %}

Заметка

Чтобы не растягивать учебник, во всех примерах шаблонов используется неполный 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 с именами переменных шаблона.

Загрузите страницу, вызвав URL-адрес «/ 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 , но если вы хотите быстро получить рабочий пример, просто напишите это:

polls/templates/polls/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 на более высоком уровне или позволить API модели генерировать исключение Http404 вместо этого ObjectDoesNotExist  ?

Потому что это свяжет уровень управления моделью со слоем представления. Одна из основных целей дизайна Django - сделать связь как можно более низкой. В модуль введена небольшая управляемая муфта django.shortcuts .

Также есть функция get_list_or_404() , которая работает аналогично get_object_or_404() , за исключением того, что она использует filter() вместо метода get() . Выдает исключение, Http404 если список пуст.

Использование системы шаблонов

Вернемся к представлению detail() нашего приложения для опросов. Учитывая переменную контекста question , шаблон polls/detail.html может выглядеть так:

polls/templates/polls/detail.html
<h1>{{ question.question_text }}</h1>
<ul>
{% for choice in question.choice_set.all %}
    <li>{{ choice.choice_text }}</li>
{% endfor %}
</ul>

Система шаблонов использует синтаксис для доступа к атрибутам переменных с помощью точек. В этом примере с Django начинает с поиска словаря в объекте . Если это не удается, он ищет атрибут, который в данном случае работает. Если бы поиск атрибутов не сработал, 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-адреса в проектах, в которых есть много шаблонов. Однако, поскольку вы определили параметр «name» в функциях path() модуля polls.urls , вы можете удалить зависимость от определенных путей URL, определенных в конфигурациях URL, с помощью тега шаблона :{% url %}

<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>

Принцип этой операции заключается в том, что URL-адрес ищется в определениях модуля polls.urls . Ниже вы можете увидеть, где именно определено URL-имя "детали":

...
# 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 %}

Ответ дается путем добавления пространств имен к вашей конфигурации URL. В файле 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  :

polls/templates/polls/index.html
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>

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

polls/templates/polls/index.html
<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>

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

Copyright ©2020 All rights reserved