Создание сайта на Python/Django: полезные Views

Продолжим рассмотрение Views в Django и переходим к написанию полезных вещей. Каждое представление отвечает за два действия – это возвращение HttpResponse с объектом данных для запрашиваемой страницы либо генерацию исключения, например, Http404. Все остальное зависит от нашей фантазии. Мы можем реализовать любой необходимый функционал, который позволяет нам Python. Будь-то чтение из БД, вывод XML или генерация PDF-файлов. При этом, мы можем использовать базовую систему шаблонов Django или подключить сторонюю по-необходимости.

Давайте изменим наше первое представление главной страницы index(), добавив него несколько строчек кода, которые будут сначала запрашивать из БД 5 последних вопросов. Затем преобразуем результат в строку используя простой метод join() и передадим результат на вывод. Выглядеть это будет так:

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)

Однако этот код содержит проблему, суть которой в том, что дизайн страницы жестко закодирован в представлении. Если нам необходимо будет изменить внешний вид страницы, то придется каждый раз редактировать код Python. Для того, чтобы этого не делать, необходимо использовать систему шаблонов Django и отделить дизайн от кода на Python. Создадим шаблон, который будет использовать наше представление.

Прежде всего, создадим каталог с именем templates в каталоге приложения polls. Джанго будет искать шаблоны в нем.
Файл настроек по умолчанию определяет так, что Django ищет шаблоны в поддиректориях каждого созданного приложения. При этом дополнительно нам необходимо создать подкаталог, соответствующий названию приложения. В конечном итоге у нас должно быть так “polls/templates/polls”.

Далее создадим файл index.html и поместим туда следующий код:

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

Осталось подключить использование данного шаблона в представлении:

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.

Теперь можем запустить наш dev-сервер и перейти на страницу “/polls/”. В результате мы должны увидеть список добавленных ранее вопросов. Ссылки указывают на страницу деталей.

Модернизируем представление при помощи render()

Загрузить шаблон, заполнить контекст и вернуть объект HttpResponse для визуализации шаблона является типичной задачей. Для упрощения Django предлагает нам функцию render().

Функция render() принимает объект запроса в качестве первого аргумента, имя шаблона в качестве второго аргумента и словарь в качестве необязательного третьего аргумента. Функция возвращает объект HttpResponse с заданным контекстом. Перепишем наше представление с использованием render():

# 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)

Как видим, код стал проще и короче, что безусловно плюс.

Возврат ошибок 404

При работе с web-приложениями часто бывает так, что выполняемый запрос не возвращает данные для отображения, т.к. их не существует. Важно, чтобы во всех таких случаях возвращалась корректная ошибка Http404. Изменим наше представление detail() следующим образом:

from django.http import HttpResponse, Http404
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 в случае, если не будет получен результат по конкретному question_id.

Чтобы этот вариант представления detail() работал, создадим файл polls/templates/polls/detail.html и поместим в него пока только следующий код:

# polls/templates/polls/detail.html
{{ question }}

Теперь, если мы в браузере введем запрос, указав неверный id вопроса, например, “/polls/555/”, то в ответ получим ответ следующего вида:

Стандартная страница ошибки 404 в Django
Стандартная страница ошибки 404 в Django

В будущем мы сможем изменить эту страницу и выводить что-то более изящное, но для текущих целей оставим как есть.

Необходимость возврата ошибки 404 в ответ на запрос несуществующих объектов является типичным для работы с Django. Для упрощения этой задачи можно использовать отдельную функцию get_object_or_404(), которая сокращает код представления. С учетом этого, модернизируем наше представление следующим образом:

from django.http import HttpResponse
from django.shortcuts import render, get_object_or_404
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_list_or_404 (), которая работает аналогично get_object_or_404 () с разницей в том, что внутри использует метод filter() вместо get(). Возвращает ошибку Http404, если список пуст.

На этом этапе мы разобрались с базовыми моментами написания представлений в Джанго. Следующим этапом будем дорабатывать нашим шаблоны, чтобы они выводили больше необходимой информации в нужном нам виде.

Добавить комментарий

Ваш адрес email не будет опубликован.

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.