Базовый шаблон проекта
Переходим в директорию шаблонов проекта и приводим base.html к следующему виду:
{% load static %}
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="author" content="{{ SITE_AUTHOR }}">
<title>{% block title %}{% endblock %} | {{ SITE_TITLE }}</title>
<!-- Bootstrap -->
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" integrity="sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN" crossorigin="anonymous">
<!-- Style -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
<link href="https://fonts.googleapis.com/css?family=Open+Sans:400,700%7CUbuntu:700&subset=cyrillic" rel="stylesheet">
<link rel="stylesheet" href="{% static 'style.css' %}">
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<!-- Include all compiled plugins (below), or include individual files as needed -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
{% block head_extra %}{% endblock %}
</head>
<body>
<header>
<nav class="navbar navbar-default navbar-static-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar-collapse" aria-expanded="false">
<span class="sr-only">Переключить навигацию</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="/">{{ SITE_AUTHOR }}</a>
</div>
<div class="collapse navbar-collapse" id="navbar-collapse">
<ul class="nav navbar-nav">
<li><a href="{% url 'books:index' %}">Книги</a></li>
<li><a href="{% url 'articles:index' %}">Статьи</a></li>
<li><a href="{% url 'questions:index' %}">Вопросы</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
{% if user.is_authenticated %}
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
<i class="fa fa-user fa-fw" aria-hidden="true"></i>
{{ user.username }}
<span class="caret"></span>
</a>
<ul class="dropdown-menu">
<li>
<a href="{% url 'accounts:change_password' %}">
<i class="fa fa-key fa-fw" aria-hidden="true"></i>
Смена пароля
</a>
</li>
<li>
<a href="{% url 'accounts:change_email' %}">
<i class="fa fa-envelope-o fa-fw" aria-hidden="true"></i>
Смена почты
</a>
</li>
<li>
<a href="{% url 'accounts:logout' %}">
<i class="fa fa-sign-out fa-fw" aria-hidden="true"></i>
Выход
</a>
</li>
</ul>
</li>
{% else %}
<li>
<a href="{% url 'accounts:login' %}">
<i class="fa fa-sign-in fa-fw" aria-hidden="true"></i>
Войти
</a>
</li>
{% endif %}
</ul>
</div>
</div>
</nav>
<div class="container">
{% block header %}
<div class="about">
<h2 class="title">{{ SITE_TITLE }}</h2>
</div>
{% endblock %}
</div>
</header>
<main>
<div class="container">
{% block main %}{% endblock %}
</div>
</main>
<footer>
<div class="container">
{% block footer %}
<p>© {{ SITE_AUTHOR }}, 2017—{% now "Y" %}</p>
<p>© Разработка и поддержка веб-сайта. <a href="https://serov.space">Сергей Серов</a>, 2017—{% now "Y" %}</p>
{% endblock %}
</div>
</footer>
</body>
</html>
Далее мы создадим базовые шаблоны для приложений статей и книг, которые будут унаследованы от вышеописанного с указанием специфичной информации о направлениях данных разделов.
Базовый шаблон статей
В директории articles создаем файл base.html:
{% extends 'base.html' %}
{% load static %}
{% block head_extra %}
<link rel="alternate" type="application/atom+xml" title="{{ SITE_TITLE }}" href="{% url 'articles:feed' %}">
{% endblock %}
{% block header %}
<div class="about">
<div class="row">
<div class="col-sm-8">
<h2 class="title">{{ SITE_TITLE }}</h2>
<h3 class="subtitle"><a href="{% url 'articles:index' %}">{{ ARTICLES_SUBTITLE }}</a></h3>
</div>
<div class="col-sm-4">
<address class="contacts">
<div class="articles-mail">
<a href="#">
<span class="sr-only">Адрес электронной почты</span>
<i class="fa fa-envelope-o fa-fw" aria-hidden="true"></i>
Почта
</a>
</div>
<div class="cv">
<a rel="nofollow" href="#">
<i class="fa fa-id-card-o fa-fw" aria-hidden="true"></i>
Резюме
</a>
</div>
</address>
</div>
</div>
<div class="description">
<p>
В течение десяти лет работы на крупных производственных предприятиях заполучил опыт создания служб с нуля,
по направлениям: охрана труда, пожарная, промышленная и экологическая безопасность.
</p>
<p>
Проведу для Вас полный аудит или разработаю комплекты документов по всем вышеперечисленным направлениям.
</p>
</div>
</div>
{% endblock %}
В файлах article.html и index.html нам необходимо заменить наследование от базового шаблона проекта на вышеописанный шаблон приложения статей.
Базовый шаблон книг
В директории templates проекта создаем папку books, а в ней base.html:
{% extends 'base.html' %}
{% block header %}
<div class="about">
<div class="row">
<div class="col-sm-8">
<h2 class="title">{{ SITE_TITLE }}</h2>
<h3 class="subtitle"><a href="{% url 'books:index' %}">{{ BOOKS_SUBTITLE }}</a></h3>
</div>
<div class="col-sm-4">
<address class="contacts">
<div class="mail">
<a href="#">
<span class="sr-only">Адрес электронной почты</span>
<i class="fa fa-envelope-o fa-fw" aria-hidden="true"></i>
Почта
</a>
</div>
<div class="donate">
<a rel="nofollow" href="#">
<span class="sr-only">Поблагодарить автора</span>
<i class="fa fa-credit-card fa-fw" aria-hidden="true"></i>
Поблагодарить
</a>
</div>
</address>
</div>
</div>
<div class="description">
<p>
С детства у меня была мечта — освоить писательское ремесло, но каждый раз меня что-то останавливало.
Теперь я достиг всего, чего хотел и настало время последнего аккорда — мокнуть в чернила свое перо.
</p>
<p>
Все жанры хороши, без исключения, но меня претит от унылой фабулы, где занудный протагонист плетется
в хвосте еще менее красочных сюжетных переплетений. Даже настоящий детектив можно спеленать
в атмосфере истового драйва.
</p>
<p>
Lights! Camera! Action!
</p>
</div>
</div>
{% endblock %}
Шаблон списка книг
Создаем файл с названием index.html:
{% extends 'books/base.html' %}
{% block head_extra %}
<link rel="alternate" type="application/atom+xml" title="{{ SITE_TITLE }}" href="{% url 'books:feed' %}">
{% endblock %}
{% block title %}{{ BOOKS_SUBTITLE }}{% endblock %}
{% block main %}
<div class="books-index">
{% if books %}
<h1>
Книги
<a href="{% url 'books:feed' %}" title="Лента обновлений">
<i class="fa fa-rss fa-fw" aria-hidden="true"></i>
</a>
</h1>
<div class="row">
{% for book in books %}
<div class="col-sm-3">
<a href="{{ book.get_absolute_url }}" class="thumbnail">
<img src="{{ book.image.url }}" alt="Обложка книги" title="{{ book.title }}">
<h3 class="sr-only">{{ book.title }}</h3>
<address class="sr-only">{{ SITE_AUTHOR }}</address>
<p class="sr-only">{{ book.slogan }}</p>
</a>
</div>
{% endfor %}
</div>
{% else %}
<h1>Видимо что-то пошло не так и все рукописи сгорели...</h1>
{% endif %}
</div>
{% endblock %}
В начале мы добавляем ленту обновлений и затем выводим список книг в виде миниатюр.
Шаблон содержимого книги
Создаем файл с названием book.html:
{% extends 'books/base.html' %}
{% block head_extra %}
{% if chapters %}
<link rel="alternate" type="application/atom+xml" title="{{ book.title }} | {{ SITE_TITLE }}" href="{% url 'books:book_feed' book.pk %}">
{% endif %}
{% endblock %}
{% block title %}{{ book.title }}{% endblock %}
{% block main %}
<div class="books-book">
<h1>
{{ book.title }}
{% if chapters %}
<a href="{% url 'books:book_feed' book.pk %}" title="Лента обновлений">
<i class="fa fa-rss fa-fw" aria-hidden="true"></i>
</a>
{% endif %}
{% if not book.completed %}
<small>В процессе</small>
{% endif %}
</h1>
<address class="sr-only">{{ SITE_AUTHOR }}</address>
<h2 class="subtitle">{{ book.slogan }}</h2>
<div>
<img src="{{ book.image.url }}" alt="Обложка книги" class="img-thumbnail img-responsive">
</div>
<div class="description">
{{ book.description|safe }}
</div>
{% if book.redirect %}
<p class="redirect">
<a href="{{ book.redirect }}" class="btn btn-primary btn-block" role="button">Подробнее</a>
</p>
{% endif %}
{% if chapters %}
<div class="chapters">
<h2>Оглавление</h2>
<div class="list-group">
{% for chapter in chapters %}
<a href="{{ chapter.get_absolute_url }}" class="list-group-item">{{ chapter.title }}</a>
{% endfor %}
</div>
</div>
{% endif %}
</div>
{% endblock %}
Выводим полное описание книги.
Добавляем ленту обновлений глав и выводим их список если присутствуют таковые.
Если книга не имеет даты окончания, значит выводим статус: в процессе.
Показываем кнопку со ссылкой на другой ресурс, если задано перенаправление (книга выложена на стороннем ресурсе).
Шаблон содержимого главы
Создаем файл с названием chapter.html:
{% extends 'books/base.html' %}
{% block title %}{{ chapter.title }} | {{ chapter.book.title }}{% endblock %}
{% block main %}
<div class="books-chapter">
<h2 class="book-title">
<a href="{{ chapter.book.get_absolute_url }}">{{ chapter.book.title }}</a>
</h2>
<address class="sr-only">{{ SITE_AUTHOR }}</address>
<h1 class="subtitle">
{{ chapter.title }}
{% if user.is_superuser %}
<a href="{{ chapter.get_admin_url }}" title="Редактировать">
<i class="fa fa-pencil fa-fw" aria-hidden="true"></i>
</a>
{% if not chapter.published %}
<small>Неопубликованная глава</small>
{% endif %}
{% endif %}
</h1>
{% if not page.has_previous %}
{% with chapter.prev as prev %}
{% if prev %}
<div class="prev-chapter">
<a href="{{ prev.get_absolute_url }}" title="{{ prev.title }}">← Перейти к предыдущей главе</a>
</div>
{% endif %}
{% endwith %}
{% endif %}
<div class="text">
{{ page|first|safe }}
</div>
{% if page.has_other_pages %}
<nav aria-label="Навигация по страницам">
<ul class="pagination">
{% if page.has_previous %}
<li>
<a href="?page={{ page.previous_page_number }}" title="Предыдущая страница">
<i class="fa fa-chevron-left" aria-hidden="true"></i>
</a>
</li>
{% endif %}
{% for number in page.paginator.page_range %}
<li{% if number == page.number %} class="active" aria-label="Текущая страница"{% endif %}>
<a href="?page={{ number }}">{{ number }}</a>
</li>
{% endfor %}
{% if page.has_next %}
<li>
<a href="?page={{ page.next_page_number }}" title="Следующая страница">
<i class="fa fa-chevron-right" aria-hidden="true"></i>
</a>
</li>
{% endif %}
</ul>
</nav>
{% endif %}
{% if not page.has_next %}
{% with chapter.next as next %}
{% if next %}
<div class="next-chapter">
<a href="{{ next.get_absolute_url }}" title="{{ next.title }}">Перейти к следующей главе →</a>
</div>
{% endif %}
{% endwith %}
{% endif %}
</div>
{% endblock %}
Выводим название книги и главы, а так же ее содержимое.
Если просматривает сайт супер-пользователь: отображаем административный блок.
Далее смотрим если глава состоит из нескольких страничек, то выводим меню переключения страниц.
Если текущая страница главы не имеет предыдущей или следующей страницы: выводим ссылки на переключение глав.
Одновременно вывод переключения страниц и глав специально отключен, для того, чтобы уберечь пользователя от случайного переключения главы, вместо страницы.
Стили
В заключение нам осталось только обновить стили, переходим в директорию static проекта и заменяем следующим содержимым:
body {
font-family: 'Open Sans', sans-serif;
font-size: 15px;
line-height: 160%;
color: black;
}
h1, h2, h3, h4, h5, h6 {
font-family: 'Ubuntu', sans-serif;
text-transform: uppercase;
font-weight: bold;
}
h1 {
font-size: 22px;
margin-bottom: 30px;
}
h2 {
font-size: 20px;
margin-bottom: 20px;
}
a:link {
text-decoration: none;
}
.subtitle {
font-size: 19px;
font-family: 'Open Sans', sans-serif;
font-weight: normal;
text-transform: none;
}
.pagination {
margin-bottom: 0;
}
.container {
max-width: 750px;
}
header .about .title {
font-size: 28px;
margin: 0;
}
header .about a {
color: black;
}
header .about .subtitle {
font-size: 18px;
text-transform: uppercase;
margin-top: 0;
}
header .about .contacts {
text-align: right;
font-size: 18px;
margin-top: 5px;
line-height: 130%;
}
header .about .contacts .articles-mail {
margin-right: 6px;
}
header .about .description {
margin-top: 15px;
}
footer {
color: #666666;
margin-bottom: 30px;
}
footer p {
margin-bottom: 0;
}
main {
margin-top: 10px;
margin-bottom: 20px;
}
main a {
font-weight: bold;
}
main form .errorlist {
list-style-type: none;
padding-left: 5px;
}
main form button {
margin-top: 10px;
}
main blockquote {
font-size: 14px;
border-color: #e6e6e6;
background-color: #f7f7f7;
}
main .django-ckeditor-widget {
width: 100%;
}
main .accounts-login .links {
margin-top: 25px;
}
main .questions-index .question .title {
font-size: 18px;
margin-top: 3px;
}
main .questions-index .pagination {
margin-top: 25px;
margin-bottom: 5px;
}
main .questions-index .your-question h2 {
margin-top: 40px;
}
main .questions-question .title {
margin-bottom: -30px;
}
main .questions-question .article {
margin-top: 50px;
}
main .questions-question .answer {
color: darkgreen;
}
main .questions-question .article .expert {
margin-right: 5px;
}
main .questions-question .article .author {
font-weight: bold;
margin-bottom: 5px;
}
main .questions-question .article .created {
color: #666666;
margin-right: 5px;
}
main .questions-question .article .text {
margin-top: 20px;
}
main .questions-question .your-answer h2 {
margin-top: 50px;
}
main .questions-question .your-answer form {
margin-top: -25px;
}
main .questions-question .your-answer label {
visibility: hidden;
}
main .articles-index {
margin-bottom: 10px;
}
main .articles-index .article .title {
font-size: 18px;
margin-top: 3px;
margin-bottom: 10px;
}
main .articles-article .title {
margin-bottom: 5px;
}
main .articles-article .published {
color: #666666;
margin-right: 5px;
}
main .articles-article .text {
margin-top: 20px;
}
main .articles-article .text h2 {
margin-top: 25px;
margin-bottom: 15px;
}
main .articles-article .text h3 {
font-size: 18px;
margin-top: 25px;
margin-bottom: 15px;
}
main .articles-article .text ul {
margin-bottom: 20px;
}
main .articles-article .text p {
margin-bottom: 15px;
}
/* books-index */
main .books-index .thumbnail h3 {
font-size: 19px;
text-transform: none;
color: black;
}
main .books-index .thumbnail p {
font-size: 17px;
font-weight: normal;
margin-top: -7px;
color: black;
}
/* books-book */
main .books-book h1 small {
text-transform: none;
}
main .books-book .subtitle {
margin-top: -26px;
margin-bottom: 35px;
}
main .books-book .description {
margin-top: 35px;
}
main .books-book .redirect {
margin-top: 30px;
}
main .books-book .chapters h2 {
margin-top: 35px;
margin-bottom: 25px;
}
/* books-chapter */
main .books-chapter .book-title {
font-size: 22px;
}
main .books-chapter .subtitle {
font-weight: bold;
margin-top: -14px;
}
main .books-chapter .prev-chapter {
margin-top: -5px;
margin-bottom: 20px;
}
main .books-chapter .next-chapter {
margin-top: 20px;
}
/* Маленькие устройства (телефоны) (<768px) */
@media (max-width: 767px) {
header .about .contacts {
margin-top: 25px;
text-align: left;
}
header .about .description {
margin-top: 10px;
}
}