Запускаємо простий блог на Wagtail CMS (Django) — частина 2

З моменту написання першої частини про Wagtail CMS вже вийшла версія 1.6.3 — саме час продовжити наш шлях по створенню простого блогу.

У першій частині ми створили тільки перший пост в блозі. Для повноцінної роботи нашого сайту цього мало, так що пора зробити повноцінний підрозділ, виведення на головну трьох останніх постів, навігацію, хлібні крихти і слайдер.

Запускаємо простий блог на Wagtail CMS (Django) - частина 2

У минулій частині ми створили базову сторінку блогу, в якій за замовчуванням є головне зображення 'main_image'.

У Wagtail логіка роботи з зображеннями досить проста. За керування зображеннями в models.py відповідає рядок:

from wagtail.wagtailimages.edit_handlers import ImageChooserPanel

В адмін.панелі додавання зображення відображається так:



Додавання зображення на сторінку визначається наступним блоком:


class BlogPage(Page):
main_image = models.ForeignKey(
'wagtailimages.Image',
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='+'
)

content_panels = Page.content_panels + [
FieldPanel('date'),
ImageChooserPanel('main_image'), #додає пункт вибору зображення в адмін.панелі сторінки
FieldPanel('intro'),
FieldPanel('body'),
]

На сторінку зображення виводитися завдяки кодом blog/templates/blog/blog_page.html:


{% load wagtailcore_tags wagtailimages_tags %}

{% if page.main_image %}
{% image page.main_image width-500 %} #вивід зображення з шириною 500px
{% endif %}

Якщо ви додали картинку для вашого поста, то повинні отримати сторінку приблизно такого виду.



Детальніше про роботу із зображеннями в wagtail можна прочитати в документації.

Тепер пора створити підрозділ, в якому будуть всі наші пости blogindexpage.html. Додамо в blog/models.py наступний код:


class BlogIndexPage(Page):
intro = RichTextField(blank=True)

content_panels = Page.content_panels + [
FieldPanel('intro', classname="full")
]

У папці з шаблонами створимо новий — blog/templates/blog/blog_index_page.html
і додамо в нього наступний код:


{% extends "base.html" %}

{% load wagtailcore_tags %}

{% block body_class %}template-blogindexpage{% endblock %}

{% block content %}
<h1>{{ page.title }}</h1>

<div class="intro">{{ page.intro|richtext }}</div>
{% endblock %}

Виконайте міграцію — python manage.py makemigrations та python manage.py migrate

Тепер в адмінці з'явився шаблон сторінки blogindexpage, в якому є тільки текстовий блок intro. Ми додамо сторінку з цим шаблоном, як дочірню головної сторінки. Раніше створену сторінку з першим постом треба буде перемістити в дочірній розділ сторінки blogindexpage

Приклад структури сайту: Головна сторінка → підрозділ блоги → сторінка поста

Нам необхідно вивести на сторінку підрозділу «блоги» всі пости, але якщо їх буде багато, то сторінка може бути дуже великий. Тому ми будемо виводити тільки 9 останніх постів, а для перегляду інших постів використовувати пагінацію django з модуля django.core.paginator.

Внесемо зміни в models.py:


from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger

class BlogIndexPage(Page):
intro = RichTextField(blank=True)

@property
def blogs(self):
# Отримати список сторінок блогу, які є нащадками цієї сторінки
blogs = BlogPage.objects.live).descendant_of(self)

# Сортувати по даті
blogs = blogs.order_by('-date')

return blogs

def get_context(self, request):
blogs = self.blogs
# Пагинация
page = request.GET.get('page')
paginator = Paginator(blogs, 9) # Показувати 9 постів
try:
blogs = paginator.page(сторінка)
except PageNotAnInteger:
blogs = paginator.page(1)
except EmptyPage:
blogs = paginator.page(paginator.num_pages)

# Оновити контекст шаблону
context = super(BlogIndexPage, self).get_context(request)
context'blogs'] = blogs
return context

content_panels = Page.content_panels + [
FieldPanel('intro', classname="full")
]

Тепер треба створити шаблон для карток постів. У папці blog/templates/blog створіть папку includes, в якій треба створити html шаблон blog_list_item.html — Приклад шляху: /blog/templates/blog/includes/blog_list_item.html

З наступним змістом:


{% load static wagtailcore_tags wagtailimages_tags %}
# Індивідуальна картка поста для розділу блоги
<div class="row media-object">
{% if blog.main_image %}
<div class="media-object-section">
{% image blog.main_image fill-200x200 as img %} #вивести зображення розміром 200px на 200px
<a href="{% pageurl blog %}"><img class="image_tmb" src="{{ img.url }}" /></a>
</div>
{% endif %}
<div class="media-object-section">
<p class="data"><strong>{{ blog.date }}</strong></p>
<a href="{% pageurl blog %}"><span>{{ blog.title }}</span></a>
<p>{{ blog.intro }}</p>
</div>
</div>


В шаблон blog/templates/blog/blog_index_page.html потрібно додати наступний код в блоці content:


{% for blog in blogs %}
{% include "blog/includes/blog_list_item.html" %}
{% empty %}
Нових постів немає
{% endfor %}

Для реалізації пагинации додаємо наступний блок на цю сторінку:


# Пагинация - використовується django.core.paginator #
<div class="row">
<ul class="pagination center">
{% if blogs.has_previous %}
<li class="waves-effect"><a href="?page={{ blogs.previous_page_number }}{% for key,value in request.GET.items %}{% ifnotequal key 'page' %}&{{ key }}={{ value }}{% endifnotequal %}{% endfor %}""><i class="material-icons">chevron_left</i></a></li>
{% endif %}
{% if blogs.has_previous %}
<li class="waves-effect"><a href="?page={{ blogs.previous_page_number }}{% for key,value in request.GET.items %}{% ifnotequal key 'page' %}&{{ key }}={{ value }}{% endifnotequal %}{% endfor %}"">{{ blogs.previous_page_number }}</a></li>
{% endif %}

{% if blogs.has_next %}
<li class="active"><a href="">{{ blogs.number }}</a></li>
<li class="waves-effect"><a href="?page={{ blogs.next_page_number }}{% for key,value in request.GET.items %}{% ifnotequal key 'page' %}&{{ key }}={{ value }}{% endifnotequal %}{% endfor %}">{{ blogs.next_page_number }}</a></li>
{% else %}
<li class="active"><a href="">{{ blogs.number }}</a></li>
{% endif %}
{% if blogs.has_next %}
<li class="waves-effect"><a href="?page={{ blogs.next_page_number }}{% for key,value in request.GET.items %}{% ifnotequal key 'page' %}&{{ key }}={{ value }}{% endifnotequal %}{% endfor %}"><i class="material-icons">chevron_right</i></a></li>
{% endif %}
</ul>
</div>

Оновлюємо все, тепер у вас повинно працювати відображення 9 останніх постів у розділі блоги, також у вас з'явилася пагинация.

Все добре, залишилося тепер додати меню, для більш легкого переходу між сторінками нашого блогу.

Меню в Wagtail працює з коробки, за нього відповідає параметр «Показувати в меню:» в розділі «Поступ», при редагуванні сторінки.

Є докладна стаття, як реалізувати меню в Wagtail.

Я коротко опишу, куди і що треба додати, щоб у вашого сайту з'явилося меню, яким можна управляти з адмін.панелі.

Додамо в base.html:


{% load home_tags %} #home_tags.py ми створимо нижче
{% block menu %}
{% get_site_root as site_root %}
{% top_menu parent=site_root calling_page=self %}
{% endblock %}

Створюємо в папці проекту /home/ папку /templatestag/ з файлом home_tags.py, з наступним змістом:


from django import template
from home.models import *
from blog.models import *

register = template.Library()

@register.assignment_tag(takes_context=True)
def get_site_root(context):
return context'request'].site.root_page

def has_menu_children(page):
return page.get_children().live).in_menu().exists()

@register.inclusion_tag('home/tags/top_menu.html', takes_context=True)
def top_menu(context, parent, calling_page=None):
menuitems = parent.get_children().live).in_menu()
for menuitem in menuitems:
menuitem.show_dropdown = has_menu_children(menuitem)
menuitem.active = (calling_page.url.startswith(menuitem.url)
if calling_page False else)
return {
'calling_page': calling_page,
'menuitems': menuitems,
'request': context'request'],
}

@register.inclusion_tag('home/tags/top_menu_children.html', takes_context=True)
def top_menu_children(context, parent):
menuitems_children = parent.get_children()
menuitems_children = menuitems_children.live).in_menu()
return {
'parent': parent,
'menuitems_children': menuitems_children,
'request': context'request'],
}

Щоб якось стилізувати меню створюємо два шаблону: top_menu.html та top_menu_children.html в папці /home/templates/home/tags/

Пишемо в top_menu.html:


{% load home_tags wagtailcore_tags %}
{% get_site_root as site_root %}
<li class="nav-item"><a href="{% pageurl site_root %}" class="menu_link_main" title="{{ site_root.title }}">Головна</a></li>
{% for menuitem in menuitems %}
<li class="nav-item">
{% if menuitem.show_dropdown %}
<a data-toggle="dropdown" class="menu_link_item" href="#">{{ menuitem.title }}</a>
{% top_menu_children parent=menuitem %}
{% else %}
<a href="{% pageurl menuitem %}">{{ menuitem.title }}</a>
{% endif %}
</li>
{% endfor %}

Пишемо в top_menu_children.html:


{% load home_tags wagtailcore_tags %}
<ul class="dropdown-menu">
<li><a href="{% pageurl parent %}">{{ parent.title }}</a></li>
{% for child in menuitems_children %}
<li><a href="{% pageurl child %}">{{ child.title }}</a></li>
{% endfor %}
</ul>

Зверніть увагу — в блоці {% load %} треба обов'язково зробити виклик вашого home_tags, інакше нічого не запрацює. І, звичайно, не забудьте прописати всі стилі меню css файл.

Нам залишилося зробити слайдер для головної сторінки, щоб вона стала веселіша й додати виведення трьох останніх постів з розділу блоги. Для цього нам знадобитися home_tags, створений раніше.

За роботу слайдера буде відповідати наступний код:

В папці /home/templates/home/ створіть папку /includes/, в ній створіть файл slider.html:


{% load wagtailimages_tags wagtailembeds_tags %}

{% if slider_items %}
<div class="slider" id="homeSlider">
<ul class="slides">
{% for slider_item in slider_items %}
<li>
{% image slider_item.image width-1200 as imageslider %}
<img src="{{ imageslider.url }}" class='home_slider' alt="{{ imageslider.alt }}" />
</li>
{% endfor %}
</ul>
</div>
{% endif %}

Для wagtail можна використовувати будь-слайдер, наприклад Nivo Slider, WOW або написати свій. У цьому уроці я використовую слайдер, який йде в комплекті з materializecss.com/media.html. Вибір слайдера для сайту справа смаку і звички. Щоб слайдер працював, не забувайте додавати всі необхідні залежності (css, js).

Далі правимо home/models.py, додаючи наступний код:


from django.db import models

from wagtail.wagtailcore.models import Page, Orderable
from wagtail.wagtailcore.fields import RichTextField
from wagtail.wagtailadmin.edit_handlers import (FieldPanel,
InlinePanel,
MultiFieldPanel,
PageChooserPanel)
from wagtail.wagtailimages.edit_handlers import ImageChooserPanel
from wagtail.wagtailsearch import index

from modelcluster.fields import ParentalKey
from wagtail.wagtailsnippets.models import register_snippet


class HomePage(Page):
body = RichTextField(blank=True)
disc_t = RichTextField(blank=True)

search_fields = Page.search_fields + [
index.SearchField('body'),
index.SearchField('disc_t'),
]

content_panels = Page.content_panels + [
InlinePanel('slider_items', label="Слайдер"), #додаємо блок управління слайдером
FieldPanel('body', classname="full"),
FieldPanel('disc_t', classname="full"),
]


class LinkFields(models.Model):
link_external = models.URLField("URL", blank=True)
link_page = models.ForeignKey(
'wagtailcore.Page',
null=True,
blank=True,
related_name='+'
)

@property
def link(self):
if self.link_page:
return self.link_page.url
else:
return self.link_external

panels = [
FieldPanel('link_external'),
PageChooserPanel('link_page'),
]

class Meta:
abstract = True

class SliderItem(LinkFields):
image = models.ForeignKey(
'wagtailimages.Image',
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='+'
)

panels = [
ImageChooserPanel('image')
]

class Meta:
abstract = True

class HomePageSliderItem(Orderable, SliderItem):
page = ParentalKey('HomePage', related_name='slider_items')


Оновлюємося, робимо міграції, перезапускаємо сервер і відкривши редагування головної сторінки побачимо новий блок зі слайдером.

Для відображення трьох останніх постів на головній сторінці, додамо наступний код:
У /home/templatestag/home_tags.py додати:


@register.inclusion_tag(
'home/tags/blog_listing_homepage.html',
takes_context=True
)
def blog_listing_homepage(context, count=3): #count=3 виводить 3 останніх поста
blogs = BlogPage.objects.live).order_by('-date') #фільтр по даті від останнього посту
return {
'blogs': blogs[:count].select_related('main_image'),
'request': context'request'],
}

В теку/home/templates/home/tags/ додати шаблон blog_listing_homepage.html, з вмістом:


{% if blogs %}
{% for blog in blogs %}
{% include "blog/includes/blog_list_item.html" %}
{% endfor %}
{% endif %}

Щоб вивести пости на головну сторінку додамо в home_page.html після слайдера:
{% blog_listing_homepage %}

Обов'язково зробіть нормальну розмітку по блокам для домашньої сторінки, щоб весь контент не висів однією купою. Робимо міграції, оновлюємося, і бачимо на головній сторінці слайдер і три останніх поста (якщо ви їх створили, звичайно)

У нас майже готовий простий блог, є головна сторінка, розділ блоги, і можна подивитися кожен пост окремо. Ми можемо створювати нові розділи, управляти постами, змінювати зображення. Для початку цього вистачає.

Додамо хлібні крихти, для кращої навігації по блогу і наш проект готовий для старту.
Реалізується це наступним кодом в шаблонах blog_page.html та blog_index_page.html:


{% if self.get_ancestors|length > 1 %}
<div class="breadcrumb_page">
{% for page in self.get_ancestors %}
{% if page.is_root == False %}
<a class="breadcrumb" href='{% pageurl page %}'>{{ page.title }}</a>
{% endif %}
{% endfor %}
<span class="breadcrumb_active">{{ self.title }}</span>
</div>
{% endif %}

Оформляємо блок стилями і отримуємо зручну навігацію в додаток до меню сайту.

Весь інший функціонал можна додати за аналогією, створити інші розділи, використовувати для різних сторінок різні шаблони, прикріпити банери та інше.

Сподіваюся, що цей базовий урок допоміг комусь почати знайомство з Wagtail CMS. У наступній частині напишу про використання StreamField.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.