Мы уже выполнили часть необходимых шагов для создания веб-сайта: мы знаем как создать модель, URL, представление и шаблон. Мы также знаем как улучшить визуальный дизайн с помощью CSS.
Время практики!
Первое, что нам потребуется в блоге - страница для отображения конкретной записи, верно?
У нас уже есть модель Post
, так что нам не нужно добавлять дополнительный код в файл models.py
.
Мы начнем с добавления ссылки внутри файла blog/templates/blog/post_list.html
. Пока он выглядит следующим образом:
{% extends "blog/base.html" %}
{% block content %}
{% for post in posts %}
<div class="post">
<div class="date">
{{ post.published_date }}
</div>
<h1><a href="">{{ post.title }}</a></h1>
<p>{{ post.text|linebreaks }}</p>
</div>
{% endfor %}
{% endblock content %}
Нам хотелось бы иметь ссылку с заголовка поста в списке на страницу подробной информации о посте. Давай изменим <h1><a href="">{{ post.title }}</a></h1>
чтобы получилась ссылка на пост:
<h1><a href="{% url "post_detail" pk=post.pk %}">{{ post.title }}</a></h1>
Самое время разобраться с загадочным {% url "post_detail" pk=post.pk %}
. Как можешь предположить, синтаксис {% %}
означает использование тегов шаблонов Django. На этот раз мы используем тот, что создаст для нас URL!
blog.views.post_detail
это путь к представлению post_detail
, которое нам нужно создать. Пожалуйста, обрати внимание: blog
- это имя нашего приложения (директория blog
), views
- имя файла views.py
без расширения и последнее - post_detail
- имя представления.
Теперь, когда мы перейдем по адресу http://127.0.0.1:8000/ мы получим ошибку (как и ожидается, поскольку у нас нет прописанного URL и представления для post_detail
). Она будет выглядеть следующим образом:
Давай создадим URL в urls.py
для представления post_detail
!
Мы хотим, чтобы адрес страницы нашего первого поста был таким: URL: http://127.0.0.1:8000/post/1/
Давай создадим URL в файле blog/urls.py
и укажем Django на представление под названием post_detail
, которое будет отображать пост целиком. Добавь строчку url(r'^post/(?P<pk>[0-9]+)/$', views.post_detail, name='post_detail'),
в файл blog/urls.py
. Файл должен выглядеть примерно так:
from django.conf.urls import include, url
from . import views
urlpatterns = [
url(r'^$', views.post_list, name='post_list'),
url(r'^post/(?P<pk>[0-9]+)/$', views.post_detail, name='post_detail'),
]
Фрагмент ^post/(?P<pk>[0-9]+)/$
выглядит страшновато, но не волнуйтесь — мы его сейчас объясним: - он начинается с ^
, что означает, как мы помним, "начало строки" - post/
значит всего лишь, что после начала строки URL должен содержать слово post и косую черту /. Пока все в порядке. - (?P<pk>[0-9]+)
- эта часть посложнее. Она означает, что Django возьмет все, что придется на эту часть строки и передаст представлению в качестве переменной pk
. [0-9]
означает, что допустимы только цифры (от 0 до 9), но не буквы. +
означает, что цифр должно быть от одной и больше. Таким образом адрес http://127.0.0.1:8000/post//
будет недействительным, а http://127.0.0.1:8000/post/1234567890/
совершенно правильным! - /
- затем нам нужен еще один символ / - $
- "конец"!
Если ты введешь адрес http://127.0.0.1:8000/post/5/
в браузер, Django должен понять, что тебе требуется представление под именем post_detail
, и передать информацию о переменной pk
(равной 5
) этому представлению.
pk
сокращение от primary key
(первичный ключ). Это имя часто используют в Django проектах. Но ты можешь назвать эту переменную как пожелаешь (помни: строчные буквы и _
вместо пробелов!). Для примера, вместо (?P<pk>[0-9]+)
мы могли бы иметь переменную post_id
, таким образом эта часть кода выглядела бы как: (?P<post_id>[0-9]+)
.
Славненько, мы добавили новый шаблон URL в файл blog/urls.py
! Давай обновим страницу: http://127.0.0.1:8000/ Бууум! Ещё одна ошибка! Как и ожидалось!
Помнишь, каким должен быть следующий шаг? Конечно: добавить представление!
В этот раз представление получит дополнительный параметр pk
. Но как дать нашему представлению знать о нем? Для этого мы определим функцию как def post_detail(request, pk):
. Обрати внимание, что мы должны использовать тоже имя переменной, что мы выбрали для обработки URL (pk
). Пропуск переменной будет неправилен и приведет к ошибке!
Теперь мы хотим получить одну конкретную запись из блога. Для этого потребуется использовать QuerySet:
Post.objects.get(pk=pk)
Однако в этом коде есть проблема. Если не существует экземпляра объекта Post
с заданным primary key
(pk
) мы получим страшную ошибку!
Мы этого не хотим! Однако, Django, конечно, имеет средство, которое позволит нам её обойти: get_object_or_404
. В случае, если не существует экземпляра объекта Post
с заданным pk
, мы получим намного более приятную страницу (которая называется Page Not Found 404
).
Хорошая новость в том, что ты можешь сделать свою страницу Page not found
. Но для нас сейчас это не самая важная задача и мы её пропустим.
Хорошо, пришло время добавить представление в файл views.py
!
Нам нужно открыть файл blog/views.py
и добавить в него следующий код:
from django.shortcuts import render, get_object_or_404
Рядом с другими строками, начинающимися с from
. В конец же файла мы добавим наше новое представление:
def post_detail(request, pk):
post = get_object_or_404(Post, pk=pk)
return render(request, 'blog/post_detail.html', {'post': post})
Именно. Теперь обнови страницу http://127.0.0.1:8000/
Заработало! Только что произойдет, если ты попробуешь перейти по ссылке из заголовка записи?
Ой, нет! Другая ошибка! Но мы уже знаем как иметь с ней дело, верно? Нам нужно добавить шаблон!
Мы создадим файл post_detail.html
в директории blog/templates/blog
.
Он должен содержать следующее:
{% extends "blog/base.html" %}
{% block content %}
<div class="post">
{% if post.published_date %}
<div class="date">
{{ post.published_date }}
</div>
{% endif %}
<h1>{{ post.title }}</h1>
<p>{{ post.text|linebreaks }}</p>
</div>
{% endblock %}
И снова мы расширяем base.html
. В блоке content
мы отображаем дату публикации (published_date, если она существует), заголовок и текст. Нам также нужно обсудить пару важных вещей, хорошо?
{% if ... %} ... {% endif %}
это тег шаблона, который мы можем использовать, если нам нужно что-то проверить (помнишь конструкцию if ... else ..
из главы Введение в Python?). В данном случае мы хотим проверить, не пуста ли дата публикации published_date
поста.
Отлично, можешь перезагрузить страницу и проверить пропала ли ошибка Page not found
.
Ура! Все работает!
Было бы неплохо проверить, что веб-сайт все еще будет работать на PythonAnywhere, верно? Давай еще раз проведем развертывание.
$ git status
$ git add -A .
$ git status
$ git commit -m "Added view and template for detailed blog post as well as CSS for the site."
$ git push
$ cd my-first-blog
$ source myvenv/bin/activate
(myvenv)$ git pull
[...]
(myvenv)$ python manage.py collectstatic
[...]
Вот и все! Поздравляем :)