Skip to content

Latest commit

 

History

History
 
 

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 

README.md

Расширяем свое приложение

Мы уже выполнили часть необходимых шагов для создания веб-сайта: мы знаем как создать модель, URL, представление и шаблон. Мы также знаем, как улучшить визуальный дизайн с помощью CSS.

Время для практики!

Первое, что нам потребуется в блоге — страница для отображения конкретной записи, верно?

У нас уже есть модель Post, так что нам не нужно добавлять дополнительный код в файл models.py.

Создадим в шаблоне ссылку на страницу поста

Мы начнём с добавления ссылки внутри файла blog/templates/blog/post_list.html. Пока он выглядит следующим образом:

{% filename %}blog/templates/blog/post_list.html{% endfilename %}

{% 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|linebreaksbr }}</p>
        </div>
    {% endfor %}
{% endblock %}

{% raw %}Нам хотелось бы иметь ссылку с заголовка поста в списке на страницу с подробной информацией о посте. Давай изменим <h1><a href="proxy.php?url=https%3A%2F%2Fgithub.com.%2F">{{ post.title }}</a></h1>, чтобы получилась ссылка на пост:{% endraw %}

{% filename %}blog/templates/blog/post_list.html{% endfilename %}

<h1><a href="{% url 'post_detail' pk=post.pk %}">{{ post.title }}</a></h1>

{% raw %}Самое время разобраться с загадочным {% url 'post_detail' pk=post.pk %}. Как можешь предположить, синтаксис {% %} означает использование тегов шаблонов Django. На этот раз мы используем тот, что создаст для нас URL!{% endraw %}

Параметр post_urls означает, что Django будет искать URL с именем post_detail в файле blog.urls.py.

А что насчёт pk=post.pk? pk — это сокращение от primary key (первичный ключ). Он уникальным образом определяет каждую запись в базе данных. Поскольку мы не задали первичного ключа в нашей модели Post, Django создал такой ключ за нас (по умолчанию это порядковый номер, то есть 1, 2, 3…) и добавил поле pk к каждой записи блога. Для получения первичного ключа мы напишем post.pk — точно так же, как мы получали значения остальных полей (title, author и т.д.) нашего объекта Post.

Теперь, когда мы перейдем по адресу http://127.0.0.1:8000/, мы получим ошибку (как и ожидается, поскольку у нас нет прописанного URL и представления для post_detail). Она будет выглядеть следующим образом:

Ошибка NoReverseMatch

Создадим URL для страницы поста

Давай создадим в urls.py URL для представления post_detail!

Мы хотим, чтобы наш первый пост отображался на странице со следующим URL-адресом: http://127.0.0.1:8000/post/1/

Давай создадим URL в файле blog/urls.py и укажем Django на представление под названием post_detail, которое будет отображать пост целиком. Добавь строчку url(r'^post/(?P<pk>\d+)/$', views.post_detail, name='post_detail'), в файл blog/urls.py. Файл должен выглядеть примерно так:

{% filename %}blog/urls.py{% endfilename %}

from django.conf.urls import url
from . import views

urlpatterns = [
    url(r'^$', views.post_list, name='post_list'),
    url(r'^post/(?P<pk>\d+)/$', views.post_detail, name='post_detail'),
]

Фрагмент ^post/(?P<pk>[0-9]+)/$ выглядит страшновато, но не волнуйся — мы его сейчас поясним:

  • он начинается с ^, что означает, как мы помним, "начало строки";
  • post/ значит всего лишь, что после начала строки URL должен содержать слово post и косую черту /. Пока всё в порядке.
  • (?P<pk>\d+) — эта часть посложнее. Она означает, что Django возьмёт всё, что придется на эту часть строки, и передаст представлению в качестве переменной pk (обрати внимание, что её имя соответствуем имени переменной, используемой в качестве первичного ключа в файле blog/templates/blog/post_list.html). \d+ означает, что допустимы только цифры (от 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) этому представлению.

Славненько, мы добавили новый шаблон URL в файл blog/urls.py! Давай обновим страницу: http://127.0.0.1:8000/. Бууум! Сервер снова перестал работать. Проверь консоль — как и ожидалось, ещё одна ошибка!

AttributeError

Помнишь, каким должен быть следующий шаг? Конечно: добавить представление!

Добавим представление для страницы поста

В этот раз представление получит дополнительный параметр pk. Но как дать нашему представлению знать о нём? Для этого мы определим функцию как def post_detail(request, pk):. Обрати внимание, что мы должны использовать то же имя переменной, что мы выбрали для обработки URL (pk). Пропуск переменной будет неправилен и приведёт к ошибке!

Теперь мы хотим получить одну конкретную запись из блога. Для этого потребуется использовать QuerySet:

{% filename %}blog/views.py{% endfilename %}

Post.objects.get(pk=pk)

Однако в этом коде есть проблема. Если не существует экземпляра объекта Post с заданным primary key (pk), мы получим страшную ошибку!

Ошибка DoesNotExist

Мы этого не хотим! Однако в Django, конечно, есть средство, которое позволит нам её обойти: get_object_or_404. В случае, если не существует экземпляра объекта Post с заданным pk, мы получим намного более приятную страницу (которая называется Page Not Found 404).

Страница не найдена

Хорошая новость состоит в том, что ты можешь сделать свою страницу Page not found. Но для нас сейчас это не самая важная задача, и мы её пропустим.

Хорошо, пришло время добавить представление в файл views.py!

В файле blog/urls.py мы создали шаблон URL под названием post_detail, который ссылался на представление под названием views.post_detail. Это значит, что Django ожидает найти функцию-представление с названием post_detail в blog/views.py.

Нам нужно открыть файл blog/views.py и добавить в него следующий код:

{% filename %}blog/views.py{% endfilename %}

from django.shortcuts import render, get_object_or_404

— рядом с другими строками, начинающимися с from. В конец же файла мы добавим наше новое представление:

{% filename %}blog/views.py{% endfilename %}

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/

Представление списка записей

Заработало! Только что произойдёт, если ты попробуешь перейти по ссылке из заголовка записи?

Ошибка TemplateDoesNotExist

Ой, нет! Другая ошибка! Но мы уже знаем, как иметь с ней дело, верно? Нам нужно добавить шаблон!

Создадим шаблон для страницы поста

Мы создадим файл post_detail.html в директории blog/templates/blog.

Он должен содержать следующее:

{% filename %}blog/templates/blog/post_detail.html{% endfilename %}

{% 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|linebreaksbr }}</p>
    </div>
{% endblock %}

И снова мы расширяем base.html. В блоке content мы отображаем дату публикации (published_date, если она существует), заголовок и текст. Нам также нужно обсудить пару важных вещей, хорошо?

{% raw %}{% if ... %} ... {% endif %} — это тег шаблона, который мы можем использовать, если нам нужно что-то проверить (помнишь конструкцию if ... else .. из главы Введение в Python?). В данном случае мы хотим проверить, не пуста ли дата публикации поста — published_date.{% endraw %}

Отлично, можешь перезагрузить страницу и проверить, пропала ли ошибка Page not found.

Отдельная страницы записи

Ура! Всё работает!

Ещё одна вещь: развёртывание!

Было бы неплохо проверить, что веб-сайт всё ещё будет работать на PythonAnywhere, верно? Давай еще раз проведём развёртывание.

{% filename %}command-line{% endfilename %}

$ git status
$ git add --all .
$ git status
$ git commit -m "Added view and template for detailed blog post as well as CSS for the site."
$ git push

Затем набери в Bash-консоли PythonAnywhere:

{% filename %}command-line{% endfilename %}

$ cd my-first-blog
$ git pull
[...]

И нажми Reload на вкладке Web.

Вот и всё! Поздравляем :)