Архив проекта: lesson-8-coolsite.zip
На данный момент
мы с вами научились создавать шаблоны, прописывать в них различные теги {% имя_тега
%}, переменные {{ имя_переменной }} и фильтры {{value|имя_фильтра}}.
Я не приводил подробное описание работы с этими элементами, так как ранее уже
создавал курс по этой теме – шаблонизатору Jinja и, при
необходимости, каждый из вас может посмотреть этот плейлист:
https://www.youtube.com/watch?v=cFJqMXxVNsI&list=PLA0M1Bcd0w8wfmtElObQrBbZjY6XeA06U
Следующий важный
шаг – научиться правильно указывать URL-адреса в наших шаблонах. На
предыдущих занятиях почти у всех ссылок я ставил заглушки – символ шарп:
<a href= "#">…</a>
Пришло время
сформировать полноценные ссылки на страницы. Для этого в Django используется
специальный тег:
{% url '<URL-адрес или имя
маргрута>' [параметры ссылки] %}
Давайте для
начала пропишем с помощью этого тега маршрут к главной странице в шаблоне base.html. В нем имеется
вот такая строчка:
<li class="logo"><a href="/"><div class="logo"></div></a></li>
Здесь косая
черта – это как раз ссылка на главную страницу. Но это не лучший подход, так
как URL-адрес главной
не обязательно должен совпадать с доменным именем (в нашем случае: http://127.0.0.1:8000/).
Мы можем определить ее и как-то иначе, например:
http://127.0.0.1:8000/home/
И тогда везде в
шаблонах придется вносить эти изменения. Гораздо практичнее и, чаще всего, так
и делают, используют имена маршрутов. Если мы откроем файл women/urls.py, то увидим, что
главная страница имеет имя home. Им мы и воспользуемся, указав в теге url:
<li class="logo"><a href="{% url 'home' %}"><div class="logo"></div></a></li>
Видите, как это
удобно. Теперь, если изменить адрес главной страницы:
urlpatterns = [
path('home/', index, name='home'),
path('about/', about, name='about'),
]
И перейти по
адресу:
http://127.0.0.1:8000/home/
то ссылка на
главную автоматически изменится. В этом удобство именованных ссылок.
Но вернем
прежний адрес главной страницы, так будет удобнее. Добавим ссылки для нашего
главного меню. Вначале переопределим список menu, указав не
только заголовок, но и имя ссылки:
menu = [{'title': "О сайте", 'url_name': 'about'},
{'title': "Добавить статью", 'url_name': 'add_page'},
{'title': "Обратная связь", 'url_name': 'contact'},
{'title': "Войти", 'url_name': 'login'}
]
Затем, в файле women/urls.py сформируем
маршруты к этим страницам:
urlpatterns = [
path('', index, name='home'),
path('about/', about, name='about'),
path('addpage/', addpage, name='add_page'),
path('contact/', contact, name='contact'),
path('login/', login, name='login'),
]
И в файле women/views.py пропишем
указанные функции представлений:
def about(request):
return render(request, 'women/about.html', {'menu': menu, 'title': 'О сайте'})
def addpage(request):
return HttpResponse("Добавление статьи")
def contact(request):
return HttpResponse("Обратная связь")
def login(request):
return HttpResponse("Авторизация")
Пока это просто
функции-заглушки. А вот функцию index перепишем в таком виде:
def index(request):
posts = Women.objects.all()
context = {
'posts': posts,
'menu': menu,
'title': 'Главная страница'
}
return render(request, 'women/index.html', context=context)
Смотрите, мы
здесь определили отдельный словарь context, в котором
указали все те параметры, что собираемся передавать шаблону index.html, а затем, в
функции render передаем этот
словарь с помощью именованного параметра context. Это будет
эквивалентно предыдущей записи, но так текст программы читается гораздо лучше.
Осталось в самом
шаблоне правильно обработать коллекцию menu. В файле
шаблона base.html пропишем
следующие строчки:
{% for m in menu %}
{% if not forloop.last %}
<li><a href="{% url m.url_name %}">{{m.title}}</a></li>
{% else %}
<li class="last"><a href="{% url m.url_name %}">{{m.title}}</a></li>
{% endif %}
{% endfor %}
Теперь, при
обновлении страницы мы получаем полноценные ссылки в главном меню.
Формирование динамических URL-адресов
Осталось
прописать ссылки у списка наших статей. Во-первых, определим шаблон маршрута,
по которому они будут доступны. Для этого в файле women/urls.py добавим строчку
в urlpatterns:
path('post/<int:post_id>/', show_post, name='post'),
то есть, маршрут
постов будет выглядеть так:
http://127.0.0.1:8000/post/<идентификатор_поста>/
Конечно, в
реальных проектах вместо post_id, как правило,
используется строка (слаг), записанная латиницей и отражающая суть статьи.
Такие ссылки лучше ранжируются поисковыми системами и понятнее пользователям
сайта. Позже мы тоже будем использовать слаг, но на данном этапе нам важно
разобраться, как создавать динамические URL-ссылки на
уровне шаблонов.
Итак, маршрут
определен, имя маршрута задано как post, осталось
прописать функцию представления show_post. Мы ее пока
определим вот такой заглушкой:
def show_post(request, post_id):
return HttpResponse(f"Отображение статьи с id = {post_id}")
Осталось все это
определить в шаблоне index.html, в котором
происходит отображение списка статей. Фактически, все что нам нужно – это
переписать следующую строчку:
<p class="link-read-post"><a href="{% url 'post' p.pk %}">Читать пост</a></p>
Смотрите, здесь
в параметре href используется
уже знакомый нам тег шаблона url, далее указываем имя ссылки post и через пробел
ее параметр post_id в виде p.pk. Вспоминаем,
что мы перебираем здесь коллекцию posts, которая хранит
ссылки на объекты класса модели Women. И у этих объектов имеется параметр pk, равный
идентификатору записи. Почему мы используем pk, а не id? В принципе,
можно указать е его, но согласно конвенции Django, лучше
использовать атрибут pk.
Если теперь
обновим главную страницу, то у всех постов появятся сформированные нами ссылки
и каждая будет соответствовать своей статье. Щелкая по любой из них, перейдем
по адресу, например:
http://127.0.0.1:8000/post/5/
и увидим строчку
«Отображение статьи с id = 5» от функции заглушки show_post. Вот так
довольно просто можно формировать различные динамические URL-адреса на
уровне шаблонов.
Функция get_absolute_url()
Как я уже не раз
отмечал, фреймворк Django работает согласно паттерну MTV (Models, Templates, Views), то есть, ему
постоянно приходится связывать модели с шаблонами и видами, а значит,
формировать URL-ссылки для
выбранных записей из таблиц БД. И мы только что это делали – формировали URL-ссылки для
записей модели Women. При разработке сайтов – это типовая
операция. Поэтому, разработчики Django озаботились упрощением и
автоматизацией этой процедуры. Что они нам предлагают? Смотрите. В классах
моделей можно определять специальный метод под названием:
get_absolute_url()
который бы
возвращал полный URL-адрес для каждой конкретной записи (ассоциированной
с текущим объектом). Например, в классе модели Women мы можем
определить этот метод следующим образом:
def get_absolute_url(self):
return reverse('post', kwargs={'post_id': self.pk})
Здесь
используется функция reverse, которая строит текущий URL-адрес записи на
основе имени маршрута post и словаря параметров kwargs. В данном
случае указан один параметр post_id со значением
идентификатора объекта self.pk. Разумеется,
вначале нам нужно импортировать эту функцию:
from django.urls import reverse
Все, теперь при
обращении к методу get_absolute_url объекта класса
модели Women, мы будем
получать ее URL-адрес.
Где здесь
упрощение и автоматизация? Смотрите, в шаблоне index.html, при
формировании ссылок статей, мы теперь можем вместо тега шаблона url указать метод get_absolute_url:
<p class="link-read-post"><a href="{{ p.get_absolute_url }}">Читать пост</a></p>
Вспоминаем, что p как раз
ссылается на объекты класса Women, следовательно, у нее появился атрибут get_absolute_url, который мы и
прописываем. И, обратите внимание, при указании этого метода, мы не пишем в конце
круглые скобки, т.к. он здесь самостоятельно не вызывается. Вызов сделает
функция render при обработке
этого шаблона.
Почему это лучше
тега url? Представьте,
что в будущем мы изменили шаблон этой ссылки и стали выводить посты не по id, а по слагу. Тогда,
при использовании тега url, нам пришлось бы менять эти ссылки в
каждом шаблоне, заменяя self.pk на self.slug, например. В
этом как раз неудобство и источник потенциальных ошибок. Теперь, с методом get_absolute_url() нам
достаточно изменить маршрут в нем и это автоматически скажется на всех шаблонах,
где она используется.
Второй важный
момент функции get_absolute_url() заключается в
том, что согласно конвенции, модули Django используют этот
метод в своей работе (если он определен в модели). Например, стандартная
админ-панель обращается к этому методу для построения ссылок на каждую запись
наших моделей. И в дальнейшем мы увидим как это работает.
В свою очередь
тег {% url %} имеет смысл
применять для построения ссылок не связанных с моделями или, для ссылок без
параметров, используя только имена маршрутов.