Разработка сайта на Django - урок 06 - создание блога
Разработка сайта на Django - урок 06 - создание блога
Продолжим разработку сайта на Django и в этом уроке реализуем функционал блога. Шаблон для блога у меня уже есть, поэтому сначала подключим его и посмотрим, какой функционал потребуется именно для шаблона.
Для подключения шаблона нам понадобиться сначала создать приложение для блога, чтобы весь функционал по нему был в одном месте. Выполняем команду:
Получаем результат:
Далее подключаем созданное приложение в файле settings.py в папке проекта booktime
В директории INSTALLED_APPS добавляем строку:
Теперь переходим в файл urls.py в папке нашего проекта
Добавляем строку.
В папке templates создаем папку blog и в ней blog.html, в котором html шаблон страницы.
Таким способом мы адрес /blog/ по которому будет открываться наш шаблон. Сейчас шаблон подгружается за счет класса TemplateView, так быстрее можно подключить шаблон и работать с ним. Правда сейчас результат следующий:
Подключенный шаблон пока не имеет стилей, поэтому делаем следующее.
Стили подключились, но медиафайлы необходимо прописывать руками.
Эта страница на которой выводятся все записи блога. Для этой страницы нам необходимо продумать реализацию следующих элементов: Title, Description, заголовок H1. Создадим модель для этой страницы.
Выполняем команды:
Если ошибок не выскочило, то выполняем миграцию.
Далее, чтобы модель появилась в админке заходим в файл admin.py в нашем приложении blog и добавляем следующее.
До этого я приводил пример добавления модели в админку при помощи декораторов, а сейчас привел второй вариант. Также зайдем в файл apps.py в приложении blog и добавим там строку.
Теперь в админке должно быть так:
Теперь настроим ссылку на страницу блога. В приложении blog создаем файл urls.py
В файл пока добавляем такие строки. После этого нам надо в файле views.py в приложении blog необходимо написать функционал, который будет вызываться и отдавать данные при заходе на адрес domain/blog/
Работает это так:
При заходе на сайт по адресу /blog/ мы сначала обращаемся к файлу urls.py в папке нашего проекта (там где лежит файл settings.py).
В файле urls.py происходит сравнение пути, к которому обращаются с url запроса.
Когда /blog/ в запросе совпадает с blog/ в файле urls.py, то инклудится приложение blog. И из приложения blog инклудится файл urls.py - за это отвечает include('blog.urls', namespace='blog'). Поскольку после /blog/ больше никаких параметров не передается, срабатывает строка, в которой пустое значение пути.
После этого должна вызваться функция или класс, которые отдадут данные в шаблон, который загрузиться при обращении по этому адресу.
Переходим в файл views.py в приложении blog.
В файле views.py мы можем написать контроллер-функцию или контроллер-класс.
Сначала приведу пример с использованием контроллер-функции
Создаем функцию с именем posts, которая принимает аргумент request. Фактически аргумент request - это экземпляр класса HttpRequest, который традиционно называется request и хранит в себе сведения о полученном клиентском запросе. Далее можно передавать именованные параметры, которые совпадают с именами URL-параметров, которые прописаны в файле urls.py.
Сама функция должна возвращать экземпляр класса HttpResponse render, дальше передается путь к шаблону и переменные, которые необходимо вывести в шаблоне.
Сейчас подключим в файле urls.py приложения blog нашу созданную функцию posts.
В некоторых примерах вы можете встретить то, что подключают контроллер-функцию from blog import views. Тогда вам придется в строке маршрута писать так: path('', views.posts, name='posts')
Что же означают значения app_name и name? Эти псевдонимы используются потом в шаблонах. Например, вам необходимо поставить ссылку на блог в шаблоне, тогда при наличии таких имен у вас ссылка будет такого вида: blog:posts. Подробнее покажу позже в статье.
После подключения нашей функции posts шаблон отображается по адресу /blog/
Далее надо вывести данные из базы данных в шаблон. У нас пока написана одна модель для блога, то начнем с нее. В файле views.py приложения блог мы дописываем 2 строки:
В строке seodata = BlogPage.objects.all() - переменная seodata получает объект модели BlogPage. Вместо all() в данном случае можно использовать first(), так как у этой модели в таблице будет всего одна запись.
В строке context = {'seodata': seodata, } мы создаем словарь, который называют контекстом шаблона. Ключу 'seodata' мы передаем значение переменной seodata, а в шаблоне уже вызываем ключ этого словаря. Поэтому вы можете называть ключ, как хотите, но все-таки понятнее, если имя будет такое же. Дальше в строке return render(request, 'blog/blog.html', context) мы передаем этот словарь, чтобы использовать его ключи в шаблоне.
Однако, мы можем использовать второй способ - это не создавать словарь context, а сразу в строке return render(request, 'blog/blog.html', {'seodata': seodata, }) сделать так.
Сейчас выведем данные в шаблон. Так как, эти данные - мета-теги для seo, то это очень интересный момент, потому что мало в каких статьях этому уделяют внимание.
Выводим мета-теги title и description в Django
Сейчас у нас реализована главная страница и страница блога со всеми статьями. Это 2 шаблона: для главной страницы шаблон main.html, а для страницы блога blog.html. Общее у этих двух шаблонов то, что есть base.html, который включает в себя header и footer, а там где header, там и head с мета-тегами и подключаемыми стилями.
Так как в файле views.py приложения blog я привел два варианта получения объекта seodata из модели BlogPage, то и в шаблоне необходимо привести пример двух вариантов. Когда мы получаем все объекты с помощью метода all() (seodata = BlogPage.objects.all()), то объект seodata итерируемый. Сейчас поясню подробнее.
Заходим в файл base.html и добавляем туда следующие строки:
Каждая из этих строк создает свой блок, в которые получает данные. Например, у нас есть
Блок с именем content в шаблоне base.html принимает в себя данные из других шаблонов. Эти данные в других шаблонах обернуты в такую конструкцию:
Точно также у нас создаются блоки title и description:
Если данные в этот блок не прилетают, то в title попадет - "Главная страница", а в description - "Мета-описание главной страницы".
Теперь заходим в шаблон blog.html и добавляем в шаблон следующее:
Так как seodata у нас итерируемый объект, то получить из него данные можно в цикле. Но если получить объект не методом all(), а например методом first(), то цикл не нужен (главное, чтобы в таблице была одна запись).
Теперь можно проверить работу наших мета-тегов title и description.
Следующей задачей будет реализовать в этом же шаблоне вывод данных для категории. Мы могли бы сделать для общей страницы блога и для страниц категорий различные функции и различные шаблоны, но это слишком просто, поэтому выведем все в один шаблон.
Для категории нам необходимо создать модель. Наши категории могут быть родительскими и дочерними, но сколько может быть дочерних? Этот вопрос для того, что я часто встречаю, что некоторые создают категории "Модель родительской категории", "модель дочерней категории". Такой подход можно найти много в каких статьях, но если иерархия вложенности может быть больше, тогда создавать под каждый уровень свою модель - очень не хочется. В этом случае правильнее использовать подход, при котором модель может ссылаться сама на себя. На самом деле у django есть специальная библиотека для этого, но мы используем функционал моделей Django.
В файле models.py приложения blog мы создаем такую модель.
Основное внимание на эту строку:
Таким способом мы можем создавать иерархию вложенности с помощью одной модели.
Далее не забываем следующие команды:
Теперь заходим в файл admin.py приложения blog и дописываем туда следующее:
Теперь мы можем зайти в админку, и создать основную категорию:
Теперь можем создать дочернюю категорию и выбрать для нее родительскую - только что созданную основную категорию:
Если интересно, то записи об этих категориях в таблице выглядят так:
Однако, у категорий нет slug, чтобы переходить по нему в нужную категорию. Добавим в модель категории slug
Поле slug лучше делать уникальным, но у нас уже были заполнены другие поля, и если мы поставим unique=True, то при проведении миграции будет ошибка, так как для новых полей необходимо вставить какое-то дефолтное значение, а оно не может быть одинаковым. Поэтому сначала ставим blank=True, а затем выполняем команды makemigrations и migrate. Иногда при команде makemigrations выскакивает предупреждение, что для новых столбцов в таблице необходимо установить дефолтное значение и необходимо выбрать одно из двух сообщений (1 или 2), а дальше ввести дефолтное значение.
После того, как миграция выполнилась успешно, заходим в админку и прописываем slug для каждой категории.
После этого в модели категории slug делаем уникальным и снова выполняем команды makemigrations и migrate.
Теперь переходим в файл views.py приложения blog и дописываем в него небольшой функционал, чтобы получать для категории и подкатегории title и description.
Наша функция posts теперь принимает два параметра: category_slug и subcategory_slug. Этим параметрам я присвоил None по умолчанию, если они вдруг пустые. Например, когда попадаем просто на страницу блога со всеми статьями, без выбора категорий.
Дальше я объявляю две переменные category и subcategory в значениях None. После этого в условии if category_slug: проверяю, есть ли в запросе slug категории или нет. Если slug категории передан в запросе, то переменная category получает объект категории из модели CategoryBlog. Метод get_object_or_404 получает из модели запись, которая соответствует условию переданных параметров. В данном случае у модели CategoryBlog идет поиск по slug. В slug прилетает значение из category_slug. В данном случае это соответствует SQL запросу:
get_object_or_404("Источник, где искать", "Условие поиска") - возвращает только одну запись или вызывает исключение Http404. В случае, если по критериям поиска найдено несколько записей, будет вызвано исключение MultipleObjectsReturned. Для получения списка записей можно использовать метод get_list_or_404.
Теперь перейдем в файл urls.py приложения blog и создадим маршруты, по которым будут работать наши категории и вызываться функция posts.
Теперь можно проверить результаты:
category
sub-category
В python нет var_dump, как в PHP, поэтому можно посмотреть, что прилетает в переменную при помощи print.
Вот что выводиться в консоль при запросе:
Теперь выведем в шаблон title и description
Тут выполняются несколько условий. Если в подкатегории (subcategory) значение title не пустое, тогда в title выводится это значение, иначе проверяется title для категории и если его нет, тогда выводится title для страницы Блог. Тоже самое и для description. Также можно реализовывать шаблонное заполнение тегов, что иногда нужно на больших сайтах.
Теперь выведем заголовки и описания в этот блок и перейдем к созданию статей.
Сейчас этот блок такой:
Делаем так:
Получаем так:
Еще реализуем хлебные крошки:
Результат следующий:
С верхней частью страницы закончили, теперь продолжим в следующей статье реализовывать статьи.
- 687