Запускаем простой блог на Wagtail CMS (Django) — часть 1 / Хабр
Являясь большим фанатом Python и фреймворка Django постоянно искал решение, как сделать разработку новых веб-проектов быстрее и удобнее.
Все, кто знаком с разработкой на Django, знают насколько неудобно строить на нем интуитивно понятную админ.панель. До мегапопулярного WordPress очень далеко, что делает порог вхождения в разработку сайтов выше, чем у PHP-фреймворков и CMS.
После долгого поиска и тестирования различных решений я нашел для себя оптимальный вариант — Wagtail CMS.
Wagtail — это полноценная CMS написанная на Django компанией Torchbox. За что им большое спасибо. Проект с открытым исходным кодом, поддерживается сообществом энтузиастов и выпускается под BSD лицензией.
Вот типичный интерфейс Wagtail:
В wagtail очень удобно работать с контентом, создавать новые публикации, разделы, работать с изображениями. Внутри этого проекта много интересных технологий. Но сегодня не об этом.
Расскажу пример по быстрому запуску простого блога для новичков в Django разработке, с учетом многих трудностей, которые могут поджидать на пути.
Нам понадобится:
1. VPS минимум с 1gb памяти
2. Базовое знакомство с django
3. Навыки в удаленной настройке Ubuntu
Шаг 1:
Вы запустили VPS сервер для разработки. Используйте Putty для удаленного подключения по SSH.
Настройка проводиться на сервере с ОС Ubuntu 14.04.
— Логинимся и начинаем базовую настройку:
sudo apt-get update sudo apt-get upgrade
Как правило, на сервере уже стоит Python 2 и 3 версии. Проверяем следующими командами:
Python -V Python3 -V
Для проекта будем использовать Python 3, так как с ним лучше работает Pillow и некоторые библиотеки, которые нам понадобятся.
Если каким-то образом у вас на сервере не оказалось python3, то ставим его командой:
sudo apt-get install python3
Так же нам потребуется python3-setuptools и python3-dev:
sudo apt-get install python3-setuptools sudo apt-get install python3-dev
Учитывая, что все ставиться на чистую систему поставим PIP (чтобы поставить свежую версию «8. 1.2» лучше делать это через easy_install3):
sudo easy_install3 pip
Проверить версию PIP можно командой:
pip -V
Если версия не самая свежая, то обновляем pip командой:
sudo pip install --upgrade pip
Чтобы в дальнейшем у нас не выпадало различных ошибок при установке Wagtail, сразу ставим все необходимые библиотеки для Pillow:
sudo apt-get install libtiff5-dev libjpeg8-dev zlib1g-dev \ libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev python-tk
Теперь нам потребуется виртуальное окружение для проекта, ставим virtualenv
sudo pip3 install virtualenv
Создаем виртуалку:
virtualenv /opt/myenv
Идем в папку /opt/myenv и запускаем виртуальную среду:
source bin/activate
Вы должны увидеть строку с названием (myenv), означающую, что виртуалка запущена:
(myenv) [email protected]:/opt/myenv$
Шаг 2: Предварительная настройка сделана, переходим к установке Wagtail CMS и настройке базы данных:
1. ставим Wagtail и Gunicorn:
sudo pip3 install wagtail gunicorn
2. Создаем наш будущий блог командой
wagtail start mysite
3. Перейдем в папку блога (mysite) и установим все зависимости для проекта из файла requirements.txt
cd mysite pip install -r requirements.txt
4. На данном этапе вы можете провести миграции и сразу запустить тестовый сервер вашего будущего блога. Но так как мы планируем сделать все правильно, то наш блог будет работать на PostgreSQL (Для django можно использовать разные базы данных, в том числе MySQL, PostgreSQL, Oracle DB и т.д). Так что проводим настройку базы данных:
sudo apt-get install libpq-dev sudo apt-get install postgresql postgresql-contrib
5. На данный момент у вас уже запущен postgresql на порту 5432. Но чтобы база данных могла взаимодействовать с Wagtail нужно поставить psycopg2:
pip install psycopg2
6. Теперь надо создать саму базу данных для проекта, для этого зайдем под пользователем postgres и проведем некоторые дополнительные настройки.
Для улучшения безопасности любых веб-проектов не рекомендуется использовать доступ к БД через postgres — суперпользователя PostgreSQL. Спасибо за комментарий immaculate
Совсем просто можно создать отдельного пользователя wagtail и базу wagtail, к которой будет доступ у приложения:
# sudo su – postgres # createdb -O wagtail wagtail
7. Так как postgresql создает пользователя wagtail не спрашивая вас про пароль, нам надо назначит ему пароль через psql:
psql
\password wagtail
Выходим из psql и закрываем сеанс под пользователем postgres:
\q exit
Проверьте, что вы еще в виртуально среде, если нет то опять активируйте виртуалку.
8. Теперь нам надо настроить Wagtail на взаимодействие c postgresql, для этого идем в base.py:
cd /opt/myenv/mysite/mysite/settings/ nano base. py
Открывается файл настроек, нам нужна запись DATABASES. Удаляем, то что есть и ставим туда следующее:
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql_psycopg2', # указываем, что база данных postgresql 'NAME': 'wagtail', # Название вашей базы данных приложения 'USER': 'wagtail', #имя пользователя, который подключается к БД 'PASSWORD': '******', 'HOST': 'localhost', 'PORT': '', } }
Кстати, в wagtail есть русский язык, так что можете заодно в этом же файле с настройками поправить параметр LANGUAGE_CODE.
LANGUAGE_CODE = 'ru-ru'
Собственно, мы провели все необходимые настройки, и теперь можно запускать миграции.
Шаг 3. Первый запуск
Перейдите в корневую папку вашего блога (cd /opt/myenv/mysite/) и начните вашу первую миграцию данных в данном проекте:
python manage.py migrate
Теперь создайте пользователя под которым вы будете заходить в админ. панель Wagtail:
python manage.py createsuperuser
Введите имя (например admin), пароль и почту.
Проверяем, что все работает:
python manage.py runserver 0.0.0.0:8000
Перейдем в браузере по ip адресу вашего VPS на 8000 порт (пример 199.199.32.32:8000) и увидим стартовую страницу wagtail.
Отлично, теперь перейдем в админ панель 199.199.32.32:8000/admin, вводим логин и пароль пользователя которые мы создали ранее и попадаем в удобный интерфейс Wagtail CMS:
Пока что наш сайт ничего не умеет, но скоро мы научим его всему необходимому для блога.
Шаг 4. Настройка стартовой страницы сайта
За главную страницу отвечает приложение «home» которое запускается из коробки. Чтобы стартовая страница была такой как вам надо внесем некоторые изменения в код.
Структура вашего сайта имеет такой вид:
mysite/ home/ migrations/ __init__.py 0001_initial. py 0002_create_homepage.py templates/ home/ home_page.html __init__.py models.py search/ templates/ search/ search.html __init__.py views.py mysite/ settings/ __init__.py base.py dev.py production.py static/ css/ mysite.css js/ mysite.js templates/ 404.html 500.html base.html __init__.py urls.py wsgi.py manage.py requirements.txt
Отредактируйте файл «home/models.py» следующим образом:
from __future__ import unicode_literals from django.db import models from wagtail.wagtailcore.models import Page from wagtail.wagtailcore.fields import RichTextField from wagtail.wagtailadmin.edit_handlers import FieldPanel class HomePage(Page): body = RichTextField(blank=True) content_panels = Page.content_panels + [ FieldPanel('body', classname="full") ]
Также отредактируйте файл home/templates/home/home_page.html:
{% extends "base.html" %} {% load wagtailcore_tags %} {% block body_class %} template-homepage {% endblock %} {% block content %} {{ page.body | richtext }} {% endblock %}
Теперь, чтобы новые изменения заработали, проведите миграции:
python manage.py makemigrations python manage.py migrate
После этого вы можете зайти в админ.панель и отредактировать текст на вашей стартовой странице.
Сохраните изменения и откройте опять ваш сайт. Вы увидите свой текст, примерно такого вида:
Для начала неплохо, но надо добавить разметку и красоту css.
За базовый шаблон отвечает файл «base.html» расположенный в mysite/settings/base.py;
За css отвечает «mysite.css» расположенный в mysite/static/css/mysite.css;
За js отвечает «mysite.js» расположенный в mysite/static/js/mysite. js;
Я поклонник materializecss и для нашего сайта мы возьмем у них шаблон и библиотеки.
Смотреть тут http://materializecss.com/getting-started.html
Вносим следующие изменения в base.html:
{% load static wagtailuserbar %} <!DOCTYPE html> <!--[if lt IE 7]> <html> <![endif]--> <!--[if IE 7]> <html> <![endif]--> <!--[if IE 8]> <html> <![endif]--> <!--[if gt IE 8]><!--> <html> <!--<![endif]--> <head> <meta charset="utf-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <title>{% block title %}{% if self.seo_title %}{{ self.seo_title }}{% else %}{{ self.title }}{% endif %}{% endblock %}{% block title_suffix %}{% endblock %}</title> <meta name="description" content="" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> {# Global stylesheets #} <link rel="stylesheet" type="text/css" href="{% static 'css/mysite. css' %}"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.6/css/materialize.min.css"> {% block extra_css %} {# Override this in templates to add extra stylesheets #} {% endblock %} </head> <body> <nav role="navigation"> <div><a href="#">Logo</a> <ul> <li><a href="#">Navbar Link</a></li> </ul> <ul> <li><a href="#">Navbar Link</a></li> </ul> <a href="#" data-activates="nav-mobile"><i>menu</i></a> </div> </nav> {% wagtailuserbar %} <div> {% block content %}{% endblock %} </div> <footer> <div> <div> <div> <h5>Company Bio</h5> <p>We are a team of college students working on this project like it's our full time job. Any amount would help support and continue development on this project and is greatly appreciated.</p> </div> <div> <h5>Settings</h5> <ul> <li><a href="#!">Link 1</a></li> <li><a href="#!">Link 2</a></li> <li><a href="#!">Link 3</a></li> <li><a href="#!">Link 4</a></li> </ul> </div> <div> <h5>Connect</h5> <ul> <li><a href="#!">Link 1</a></li> <li><a href="#!">Link 2</a></li> <li><a href="#!">Link 3</a></li> <li><a href="#!">Link 4</a></li> </ul> </div> </div> </div> <div> <div> Made by <a href="http://materializecss. com">Materialize</a> </div> </div> </footer> <!-- Scripts--> <script src="https://code.jquery.com/jquery-2.1.1.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.6/js/materialize.min.js"></script> {# Global javascript #} <script type="text/javascript" src="{% static 'js/mysite.js' %}"></script> {% block extra_js %} {# Override this in templates to add extra javascript #} {% endblock %} </body> </html>
Сохраняем и снова открываем наш сайт. Должно получиться, что-то похожее на эту картинку:
Уже лучше. Теперь нам надо создать блог, ради которого все и затевалось.
Шаг 5. Создание блога.
Идем опять в корень нашего проекта и выполняем следующую команду:
python manage.py startapp blog
После этого добавьте новое приложение «blog» в INSTALLED_APPS в mysite/settings/base. py.
Для каждой записи нашего блога будут стандартные поля — заголовок, дата, картинка, введение и основной текст. Чтобы все это работало внесите следующие изменения в «blog/models.py»
from django.db import models from wagtail.wagtailcore.models import Page from wagtail.wagtailcore.fields import RichTextField from wagtail.wagtailadmin.edit_handlers import FieldPanel from wagtail.wagtailimages.edit_handlers import ImageChooserPanel from wagtail.wagtailsearch import index class BlogPage(Page): main_image = models.ForeignKey( 'wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+' ) date = models.DateField("Post date") intro = models.CharField(max_length=250) body = RichTextField(blank=True) search_fields = Page.search_fields + [ index.SearchField('intro'), index.SearchField('body'), ] content_panels = Page.content_panels + [ FieldPanel('date'), ImageChooserPanel('main_image'), FieldPanel('intro'), FieldPanel('body'), ]
Теперь создайте саму страницу, на которую все это будет выводиться в blog/templates/blog/blog_page. html:
{% extends "base.html" %} {% load wagtailcore_tags wagtailimages_tags %} {% block body_class %}template-blogpage{% endblock %} {% block content %} <h2>{{ page.title }}</h2> <p>{{ page.date }}</p> {% if page.main_image %} {% image page.main_image width-500 %} {% endif %} <div>{{ page.intro }}</div> {{ page.body|richtext }} {% endblock %}
Пока что ничего не работает, поэтому выполняем команды
python manage.py makemigrations python manage.py migrate
Теперь можно создать первую запись в нашем блоге, через удобный интерфейс wagtail.
Идем в админ.панель, открываем «Проводник», выбираем главную, и нажимаем «добавить дочернюю страницу», появиться следующее окно с выбором шаблонов для создаваемой страницы:
Выбираем «Blog Page» и заполняем страницу информацией.
Вот пример редактирования страницы блога в админ.панели wagtail:
Довольно удобно и быстро.
Сохраняем запись и открываем в браузере. Первая запись вашего блога готова.
Если хоть кому-то данный пост окажется полезным, то я продолжу публикацию постов на тему wagtail, в которой расскажу подробнее про различные возможности данной CMS.
Часть 2
Часть 3
Для всех заинтересовавшихся Wagtail CMS:
Официальный сайт https://wagtail.io/
Гитхаб проекта https://github.com/torchbox/wagtail/
Документация http://docs.wagtail.io/en/latest/index.html
Демосайт для изучения http://docs.wagtail.io/en/latest/getting_started/demo_site.html
Группа поддержки пользователей https://groups.google.com/forum/#!forum/wagtail
Создание сайта на Wagtail (CMS на Django) сайт на Python 3.7
В мире Python концепт CMS, не похож с тем что вы возможно встречали в PHP (WordPress). Разобраться в PHP с готовым CMS гораздо легче чем в Python. На данный момент существуют несколько CMS которые используют фреймворк Django, самые популярные из них это django-cms и Wagtail. В данной статье мы будем выполнять первые шаги к собственному сайту на Python используя Wagtail.
Настройка сервера (VPS)
Обычный хостинг для такого проекта будет недостаточно. Если для блога на PHP достаточно заказать обычный хостинг, то для веб проекта на Python мы рекомендуем полноценный VPS на операционной системе Linux от Fornex.com Мы долгое время пользуемся их услугами для нескольких наших проектов и можем рекомендовать их.
Заказ VPS
Ссылка на хостинг: Fornex
После регистрации и входа в ваш личный кабинет, у вас появится возможность заказать VPS и указать необходимые настройки. В моем случае это SSD CLOUD 1GB на операционной системе Ubuntu 18.04 LTS (это самая актуальная версия на момент написания статьи, советуем всегда выбирает самые свежие версии).
Если вы профи в Linux, то можете выбрать настройку «Без панели» при выборе панели управления, но если вы хотите иметь некий интерфейс настроек, то советуем выбрать панель управления «Vesta CP«.
Подключаемся по SSH и выполняем необходимые команды в консоли.
$ apt update $ apt upgrade
$ apt update $ apt upgrade |
На VPS у меня уже был установлен Python 3.6.7 но если по каким либо причинам его у вас нет, то устанавливаем его таким образом:
$ apt install python3
$ apt install python3 |
Далее, устанавливаем необходимые библиотеки:
$ apt install python3-setuptools python3-dev python3-venv $ apt install libtiff5-dev libjpeg8-dev zlib1g-dev $ apt install libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev python-tk
$ apt install python3-setuptools python3-dev python3-venv $ apt install libtiff5-dev libjpeg8-dev zlib1g-dev $ apt install libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev python-tk |
Не забываем про pip:
$ apt install python3-pip
$ apt install python3-pip |
Все манипуляции и установки через pip выполняем исключительно в виртуальном окружении, чтобы не засорять ненужными библиотеками главное окружение. В папке /home
создаем новую папку /home/web
cd /home mkdir web cd web
cd /home mkdir web cd web |
Находясь в папке /home/web
мы создадим виртуальное окружение для нашего приложения:
python3 -m venv venv source venv/bin/activate
python3 -m venv venv source venv/bin/activate |
Вводная строка терминала изменилась на что-то подобное:
(venv) user@kvmde58-14996:/home/web#
(venv) user@kvmde58-14996:/home/web# |
Установка Wagtail и его зависимостей:
$ pip install wagtail
$ pip install wagtail |
Запустите сайт:
$ wagtail start mysite $ cd mysite
$ wagtail start mysite $ cd mysite |
Wagtail предоставляет команду start
, аналогичную django-admin. py startproject
. Запуск wagtail start mysite
в вашем проекте создаст новую папку mysite
со специальными дополнениями Wagtail, включая необходимые настройки проекта, приложение “home” с пустой домашней страницей, основными шаблонами и примером приложения “search”.
Установка проектных зависимостей
$ pip install -r requirements.txt
$ pip install -r requirements.txt |
Здесь указывается, что у вас подходящая версия Django для созданного вами проекта.
Создание базы данных
Python
$ python manage.py migrate
$ python manage.py migrate |
Если вы не обновили настройки проекта, то у вас будет файл базы данных SQLite в папке проекта.
Создание пользователя admin:
$ python manage.py createsuperuser
$ python manage. py createsuperuser |
Запуск сервера:
$ python manage.py runserver
$ python manage.py runserver |
Если все сработало, то http://127.0.0.1:8000
покажет страницу приветствия:
Вы можете перейти к административному разделу в http://127.0.0.1:8000/admin
Расширяем модель домашней страницы
Изначально, приложение “home” определяет пустую модель домашней страницы в models.py
, наряду с миграцией, которая создает домашнюю страницу и указывает Wagtail использовать ее.
Измените home/models.py
как указано внизу, чтобы внести поле body
в модель:
Python
from django.db import models from wagtail.core.models import Page from wagtail.core.fields import RichTextField from wagtail.admin.edit_handlers import FieldPanel class HomePage(Page): body = RichTextField(blank=True) content_panels = Page. content_panels + [ FieldPanel(‘body’, classname=»full»), ]
1 2 3 4 5 6 7 8 9 10 11 12 13 | from django.db import models
from wagtail.core.models import Page from wagtail.core.fields import RichTextField from wagtail.admin.edit_handlers import FieldPanel
class HomePage(Page): body = RichTextField(blank=True)
content_panels = Page.content_panels + [ FieldPanel(‘body’, classname=»full»), ] |
body
определено как RichTextField
, специальное поле Wagtail. Вы можете использовать любые поля Django. content_panels
определяет возможности и макет интерфейса редактирования.
Запустите manage.py makemigrations
, затем python manage.py migrate
, чтобы обновить базу данных с изменениями вашей модели. Вам следует вводить указанные команды каждый раз, когда вы вносите изменения в модели.
Теперь вы можете редактировать домашнюю страницу внутри раздела администратора Wagtail (переходить на Pages, Homepage и Edit), чтобы увидеть новое поле body
. Введите какой-нибудь текст в поле body, и опубликуйте страницу.
Шаблон страницы теперь ждет обновления для отображения изменений, внесенных в модель. Wagtail использует обычные шаблоны Django для рендера каждого типа страницы. По умолчанию, он будет искать название шаблона, состоящего из названий приложения и модели, с разделенными нижним подчеркиванием названиями (например, HomePage внутри приложения “home” называется home/home_page.html
). Этот файл шаблона может существовать в любой распознаваемой правилами шаблонов Django локации. Условно, он помещает под папкой с шаблонами внутри приложения.
Измените home/templates/home/home_page. html
:
{% extends «base.html» %} {% load wagtailcore_tags %} {% block body_class %}template-homepage{% endblock %} {% block content %} {{ page.body|richtext }} {% endblock %}
1 2 3 4 5 6 7 8 9 | {% extends «base.html» %}
{% load wagtailcore_tags %}
{% block body_class %}template-homepage{% endblock %}
{% block content %} {{ page.body|richtext }} {% endblock %} |
Теги шаблонов Wagtail
Wagtail предоставляет ряд тегов шаблонов и фильтров, которые можно загрузить внесением {% load wagtailcore_tags %}
в начале вашего файла шаблона.
В данном руководстве мы используем фильтр richtext
для экранирования и вывода содержимого RichTextField
:
{% load wagtailcore_tags %} {{ page.body|richtext }}
{% load wagtailcore_tags %} {{ page. body|richtext }} |
Генерирует:
<div> <p> <b>Welcome</b> to our new site! </p> </div>
<div> <p> <b>Welcome</b> to our new site! </p> </div> |
Обратите внимание: вам нужно будет вставлять {% load wagtailcore_tags %}
в каждый шаблон, который использует теги Wagtail. Django будет выдавать ошибку TemplateSyntaxError, если теги не будут загружены.
Пример: Простой блог
С этого момента мы можем приступить к созданию блога. Чтобы сделать это, запустите python manage.py startapp blog
, чтобы создать новое приложение в вашем сайте Wagtail.
Внесите новое приложение blog
в INSTALLED_APPS
в mysite/settings/base.py
.
Главная страница блога и посты
Давайте начнем с простой страницы индекса для нашего блога. В blog/models.py
:
Python
from wagtail.core.models import Page from wagtail.core.fields import RichTextField from wagtail.admin.edit_handlers import FieldPanel class BlogIndexPage(Page): intro = RichTextField(blank=True) content_panels = Page.content_panels + [ FieldPanel(‘intro’, classname=»full») ]
1 2 3 4 5 6 7 8 9 10 11 | from wagtail.core.models import Page from wagtail.core.fields import RichTextField from wagtail.admin.edit_handlers import FieldPanel
class BlogIndexPage(Page): intro = RichTextField(blank=True)
content_panels = Page.content_panels + [ FieldPanel(‘intro’, classname=»full») ] |
Запустите python manage.py makemigrations
и python manage.py migrate
.
Так как модель называется BlogIndexPage
, название шаблона по умолчанию (если мы не меняли его) будет blog/templates/blog/blog_index_page.html
. Создайте этот файл со следующим содержимым:
{% extends «base.html» %} {% load wagtailcore_tags %} {% block body_class %}template-blogindexpage{% endblock %} {% block content %} <h2>{{ page.title }}</h2> <div>{{ page.intro|richtext }}</div> {% for post in page.get_children %} <h3><a href=»{% pageurl post %}»>{{ post.title }}</a></h3> {{ post.specific.intro }} {{ post.specific.body|richtext }} {% endfor %} {% endblock %}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | {% extends «base.html» %}
{% load wagtailcore_tags %}
{% block body_class %}template-blogindexpage{% endblock %}
{% block content %} <h2>{{ page. title }}</h2>
<div>{{ page.intro|richtext }}</div>
{% for post in page.get_children %} <h3><a href=»{% pageurl post %}»>{{ post.title }}</a></h3> {{ post.specific.intro }} {{ post.specific.body|richtext }} {% endfor %}
{% endblock %} |
Большая часть содержимого должна быть вам знакома, но мы объясним что делает get_children
немного позже. Обратите внимание на тег pageurl
, который аналогичен тегу url
в Django, но принимает объект страницы Wagtail в качестве аргумента.
В админке Wagtail создайте BlogIndexPage
в качестве дочернего элемента Homepage, убедитесь, что у него есть слаг “blog” во вкладке Promote и опубликуйте его. Теперь у вас должен появиться доступ к url /blog
на вашем сайте (обратите внимание на то, как слаг из панели Promote определяет URL страницы).
Теперь нам нужна модель и шаблон для наших постов в блоге. В blog/models.py
:
Python
from django.db import models from wagtail.core.models import Page from wagtail.core.fields import RichTextField from wagtail.admin.edit_handlers import FieldPanel from wagtail.search import index # Сохраняем определение BlogIndexPage и вносим: class BlogPage(Page): date = models.DateField(«Post date») intro = models.CharField(max_length=250) body = RichTextField(blank=True) search_fields = Page.search_fields + [ index.SearchField(‘intro’), index.SearchField(‘body’), ] content_panels = Page.content_panels + [ FieldPanel(‘date’), FieldPanel(‘intro’), FieldPanel(‘body’, classname=»full»), ]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | from django. db import models
from wagtail.core.models import Page from wagtail.core.fields import RichTextField from wagtail.admin.edit_handlers import FieldPanel from wagtail.search import index
# Сохраняем определение BlogIndexPage и вносим:
class BlogPage(Page): date = models.DateField(«Post date») intro = models.CharField(max_length=250) body = RichTextField(blank=True)
search_fields = Page.search_fields + [ index.SearchField(‘intro’), index.SearchField(‘body’), ]
content_panels = Page.content_panels + [ FieldPanel(‘date’), FieldPanel(‘intro’), FieldPanel(‘body’, classname=»full»), ] |
Запускаем в терминале python manage.py makemigrations
и python manage.py migrate
.
Создаем шаблон в blog/templates/blog/blog_page. html
:
{% extends «base.html» %} {% load wagtailcore_tags %} {% block body_class %}template-blogpage{% endblock %} {% block content %} <h2>{{ page.title }}</h2> <p>{{ page.date }}</p> <div>{{ page.intro }}</div> {{ page.body|richtext }} <p><a href=»{{ page.get_parent.url }}»>Return to blog</a></p> {% endblock %}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | {% extends «base.html» %}
{% load wagtailcore_tags %}
{% block body_class %}template-blogpage{% endblock %}
{% block content %} <h2>{{ page.title }}</h2> <p>{{ page.date }}</p>
<div>{{ page.intro }}</div>
{{ page.body|richtext }}
<p><a href=»{{ page. get_parent.url }}»>Return to blog</a></p>
{% endblock %} |
Обратите внимание на то, что мы использовали метод get_parent()
для получения URL блога, частью которого является этот пост.
Теперь создаем несколько постов в блоге в качестве дочерних элементов BlogIndexPage
. Убедитесь в том, что выбрали “Blog Page” при создании ваших постов.
Wagtail дает вам полный контроль над тем, какой тип содержимого может быть создан под различными родительскими типами контента. По умолчанию, любая страница может быть дочерней любого типа страницы.
Теперь у вас в распоряжении базовый рабочий блог. Перейдите по URL /blog
и увидите что-нибудь вроде следующего:
Заголовки должны переводить к страницам постов, а ссылка на главную страницу должна появиться в футере каждой страницы поста.
Дочерние и родительские элементы
Большая часть работы, выполняемая в Wagtail вертится вокруг концепции иерархии структуры “дерева”, состоящего из ветвей и листьев. В данном случае, BlogIndexPage
— ветвь, а экземпляры страниц блога являются листьями.
Рассмотрим blog_index_page.html
изнутри:
{% for post in page.get_children %} <h3><a href=»{% pageurl post %}»>{{ post.title }}</a></h3> {{ post.specific.intro }} {{ post.specific.body|richtext }} {% endfor %}
{% for post in page.get_children %} <h3><a href=»{% pageurl post %}»>{{ post.title }}</a></h3> {{ post.specific.intro }} {{ post.specific.body|richtext }} {% endfor %} |
Каждая “страница” в Wagtail может вызывать дочернюю или родительскую страницу со своей позиции в иерархии. Но почему мы должны определять post.specific.intro
вместо post.intro
? Это напрямую связано с тем, как мы определили нашу модель:
Python
class BlogPage(Page):
class BlogPage(Page): |
Метод get_children()
выдает нам список экземпляров основного класса Page
. Когда нам нужно сослаться на свойства экземпляров, которые наследуются от базового класса, Wagtail предоставляет особый метод, который возвращает фактическую запись BlogPage
. В то время как поле “title” присутствует в базовой модели Page
, “intro” присутствует только в модели BlogPage
, так что нам нужен .specific
для получения доступа.
Чтобы сжать код шаблона, мы можем использовать тег Django под названием with
:
Python
{% for post in page.get_children %} {% with post=post.specific %} <h3><a href=»{% pageurl post %}»>{{ post.title }}</a></h3> <p>{{ post.intro }}</p> {{ post.body|richtext }} {% endwith %} {% endfor %}
1 2 3 4 5 6 7 | {% for post in page.get_children %} {% with post=post. specific %} <h3><a href=»{% pageurl post %}»>{{ post.title }}</a></h3> <p>{{ post.intro }}</p> {{ post.body|richtext }} {% endwith %} {% endfor %} |
Когда вы начинаете вписывать больше персонализированного когда в Wagtail, вы увидите целый набор модификаторов QuerySet
, которые помогут вам ориентироваться по иерархии.
Python
# Задан объект страницы ‘somepage’: MyModel.objects.descendant_of(somepage) child_of(page) / not_child_of(somepage) ancestor_of(somepage) / not_ancestor_of(somepage) parent_of(somepage) / not_parent_of(somepage) sibling_of(somepage) / not_sibling_of(somepage) # … и далее… somepage.get_children() somepage.get_ancestors() somepage.get_descendants() somepage.get_siblings()
1 2 3 4 5 6 7 8 9 10 11 12 | # Задан объект страницы ‘somepage’: MyModel. objects.descendant_of(somepage) child_of(page) / not_child_of(somepage) ancestor_of(somepage) / not_ancestor_of(somepage) parent_of(somepage) / not_parent_of(somepage) sibling_of(somepage) / not_sibling_of(somepage)
# … и далее… somepage.get_children() somepage.get_ancestors() somepage.get_descendants() somepage.get_siblings() |
Переопределение контекста
Есть небольшие проблемы с главной страницей нашего блога:
- Контент в блогах (как правило) показывается в обратном хронологическом порядке;
- Нам нужно быть уверенными в том, что мы показываем только опубликованный контент (без черновиков).
Чтобы достичь этих целей, нам нужно сделать нечто большее, чем просто взять дочерние элементы страниц индекса в шаблоне.
Вместо этого, нам нужно будет обновить QuerySet
в определении модели. Wagtail позволяет сделать это просто, при помощи переопределяемого метода get_context()
. Изменение модели BlogIndexPage
проходит следующим образом:
Python
class BlogIndexPage(Page): intro = RichTextField(blank=True) def get_context(self, request): # Обновляем контекст для внесения только опубликованных постов в обратном хронологическом порядке context = super().get_context(request) blogpages = self.get_children().live().order_by(‘-first_published_at’) context[‘blogpages’] = blogpages return context
1 2 3 4 5 6 7 8 9 | class BlogIndexPage(Page): intro = RichTextField(blank=True)
def get_context(self, request): # Обновляем контекст для внесения только опубликованных постов в обратном хронологическом порядке context = super().get_context(request) blogpages = self.get_children().live().order_by(‘-first_published_at’) context[‘blogpages’] = blogpages return context |
Все что мы сделали здесь, это вернули оригинальный контекст, создали персональный QuerySet
, внесли его в полученный контекст, и вернули обновленный контекст обратно в представление. Вам также нужно будет немного обновить ваш шаблон blog_index_page.html
. Измените следующее:
{% for post in page.get_children %}
на {% for post in blogpages %}
Теперь попробуйте отменить публикацию одного из ваших постов — он должен исчезнуть с главной страницы блога. Оставшиеся посты должны отсортированы начиная с самого нового.
Изображения
Давайте добавим возможность внесения галереи изображений в наши посты. Хотя мы можем просто вставлять картинки в тело текста, все же есть несколько преимуществ в настройке галереи изображений как нового выделенного типа объекта внутри базы данных.
Таким образом, у вас будет полный контроль над макетами и стилями изображений в шаблоне, вместо того, чтобы выкладывать их определенным образом в поле текста. Это также позволяет использовать изображения в различных местах, вне зависимости от текста блога. Например, в оглавлении на главной странице блога.
Добавим новую модель BlogPageGalleryImage
в models. py
:
Python
from django.db import models # Новые импорты добавлены для ParentalKey, Orderable, InlinePanel, ImageChooserPanel from modelcluster.fields import ParentalKey from wagtail.core.models import Page, Orderable from wagtail.core.fields import RichTextField from wagtail.admin.edit_handlers import FieldPanel, InlinePanel from wagtail.images.edit_handlers import ImageChooserPanel from wagtail.search import index # … (Оставляем модель BlogIndexPage без изменений, и обновляем BlogPage:) class BlogPage(Page): date = models.DateField(«Post date») intro = models.CharField(max_length=250) body = RichTextField(blank=True) search_fields = Page.search_fields + [ index.SearchField(‘intro’), index.SearchField(‘body’), ] content_panels = Page.content_panels + [ FieldPanel(‘date’), FieldPanel(‘intro’), FieldPanel(‘body’, classname=»full»), InlinePanel(‘gallery_images’, label=»Gallery images»), ] class BlogPageGalleryImage(Orderable): page = ParentalKey(BlogPage, on_delete=models. CASCADE, related_name=’gallery_images’) image = models.ForeignKey( ‘wagtailimages.Image’, on_delete=models.CASCADE, related_name=’+’ ) caption = models.CharField(blank=True, max_length=250) panels = [ ImageChooserPanel(‘image’), FieldPanel(‘caption’), ]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | from django.db import models
# Новые импорты добавлены для ParentalKey, Orderable, InlinePanel, ImageChooserPanel
from modelcluster.fields import ParentalKey
from wagtail.core.models import Page, Orderable from wagtail. core.fields import RichTextField from wagtail.admin.edit_handlers import FieldPanel, InlinePanel from wagtail.images.edit_handlers import ImageChooserPanel from wagtail.search import index
# … (Оставляем модель BlogIndexPage без изменений, и обновляем BlogPage:)
class BlogPage(Page): date = models.DateField(«Post date») intro = models.CharField(max_length=250) body = RichTextField(blank=True)
search_fields = Page.search_fields + [ index.SearchField(‘intro’), index.SearchField(‘body’), ]
content_panels = Page.content_panels + [ FieldPanel(‘date’), FieldPanel(‘intro’), FieldPanel(‘body’, classname=»full»), InlinePanel(‘gallery_images’, label=»Gallery images»), ]
class BlogPageGalleryImage(Orderable): page = ParentalKey(BlogPage, on_delete=models. CASCADE, related_name=’gallery_images’) image = models.ForeignKey( ‘wagtailimages.Image’, on_delete=models.CASCADE, related_name=’+’ ) caption = models.CharField(blank=True, max_length=250)
panels = [ ImageChooserPanel(‘image’), FieldPanel(‘caption’), ] |
Запускаем manage.py makemigrations
и python manage.py migrate
.
Здесь есть несколько новых концепций, давайте рассмотрим их все сразу:
Наследование от Orderable
добавляет поле sort_order
в модель, чтобы отслеживать порядок изображений в галерее.
ParentalKey
от BlogPage
отвечает за прикрепление изображений галереи в определенную страницу. ParentalKey
работает аналогично ForeignKey
, но также рассматривается как фундаментальная часть страницы в таких операциях, как отправка на модерацию и отслеживание истории изменений.
image
является ForeignKey
во встроенной модели Wagtail под названием Image
, в которой хранятся изображения. Это включает выделенный тип панели ImageChooserPanel
, который предоставляет всплывающий интерфейс для выбора существующего изображения или выбора нового. Таким образом, мы позволяем изображению существовать в нескольких галереях — фактически, мы создали мульти-отношение между страницами и изображениями.
Указание on_delete=models.CASCADE
для внешнего ключа означает, что если изображение было удалено из системы, оно также будет удалено из галереи. (В других ситуациях, было бы разумным оставлять изображение в галерее, например если речь идет о странице “наши сотрудники”, где размещены снимки сотрудников, и одна из фотографий удалена по той или иной причине — было бы неплохо оставить это изображение в базе. В данном случае, мы меняем внешний ключ на blank=True, null=True, on_delete=models.SET_NULL
. )
Наконец, внесение InlinePanel
в BlogPage.content_panels
делает изображения галереи доступными для интерфейса редактирования в BlogPage
.
Настройте шаблон страницы блога, чтобы включить изображения:
{% extends «base.html» %} {% load wagtailcore_tags wagtailimages_tags %} {% block body_class %}template-blogpage{% endblock %} {% block content %} <h2>{{ page.title }}</h2> <p>{{ page.date }}</p> <div>{{ page.intro }}</div> {{ page.body|richtext }} {% for item in page.gallery_images.all %} <div> {% image item.image fill-320×240 %} <p>{{ item.caption }}</p> </div> {% endfor %} <p><a href=»{{ page.get_parent.url }}»>Return to blog</a></p> {% endblock %}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | {% extends «base. html» %}
{% load wagtailcore_tags wagtailimages_tags %}
{% block body_class %}template-blogpage{% endblock %}
{% block content %} <h2>{{ page.title }}</h2> <p>{{ page.date }}</p>
<div>{{ page.intro }}</div>
{{ page.body|richtext }}
{% for item in page.gallery_images.all %} <div> {% image item.image fill-320×240 %} <p>{{ item.caption }}</p> </div> {% endfor %}
<p><a href=»{{ page.get_parent.url }}»>Return to blog</a></p>
{% endblock %} |
Здесь мы используем тег {% image %}
(который существует в библиотеке wagtailimages_tags
, импортированной вверху шаблона) для внесения элемента с параметром fill-320x240
чтобы отметить, что изображение должно попадать под прямоугольник размером 320х240.
Так как изображения нашей галереи являются объектами базы данных сами по себе, мы можем запрашивать и повторно использовать их, вне зависимости от тела поста в блоге. Давайте определим метод main_image
, который возвращает изображение из первого элемента галереи (или None
, если в галерее нет элементов):
Python
class BlogPage(Page): date = models.DateField(«Post date») intro = models.CharField(max_length=250) body = RichTextField(blank=True) def main_image(self): gallery_item = self.gallery_images.first() if gallery_item: return gallery_item.image else: return None search_fields = Page.search_fields + [ index.SearchField(‘intro’), index.SearchField(‘body’), ] content_panels = Page.content_panels + [ FieldPanel(‘date’), FieldPanel(‘intro’), FieldPanel(‘body’, classname=»full»), InlinePanel(‘gallery_images’, label=»Gallery images»), ]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | class BlogPage(Page): date = models. DateField(«Post date») intro = models.CharField(max_length=250) body = RichTextField(blank=True)
def main_image(self): gallery_item = self.gallery_images.first() if gallery_item: return gallery_item.image else: return None
search_fields = Page.search_fields + [ index.SearchField(‘intro’), index.SearchField(‘body’), ]
content_panels = Page.content_panels + [ FieldPanel(‘date’), FieldPanel(‘intro’), FieldPanel(‘body’, classname=»full»), InlinePanel(‘gallery_images’, label=»Gallery images»), ] |
Этот метод теперь доступен в наших шаблонах. Обновите blog_index_page.html
для внесения главного изображения в анонс рядом с каждым изображением:
{% load wagtailcore_tags wagtailimages_tags %} … {% for post in blogpages %} {% with post=post. specific %} <h3><a href=»{% pageurl post %}»>{{ post.title }}</a></h3> {% with post.main_image as main_image %} {% if main_image %}{% image main_image fill-160×100 %}{% endif %} {% endwith %} <p>{{ post.intro }}</p> {{ post.body|richtext }} {% endwith %} {% endfor %}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | {% load wagtailcore_tags wagtailimages_tags %}
…
{% for post in blogpages %} {% with post=post.specific %} <h3><a href=»{% pageurl post %}»>{{ post.title }}</a></h3>
{% with post.main_image as main_image %} {% if main_image %}{% image main_image fill-160×100 %}{% endif %} {% endwith %}
<p>{{ post. intro }}</p> {{ post.body|richtext }} {% endwith %} {% endfor %} |
Метки постов
Скажем, нам нужно дать возможность редакторам “отмечать” их посты, чтобы читатель мог просматривать тематический контент. Для этого, нам нужно вызвать систему тегов, предоставляемую в комплекте с Wagtail, прикрепить ее к модели BlogPage
и панелям контента, отобразить связанные теги в шаблоне поста. Разумеется, нам понадобится рабочий вид URL для конкретных тегов.
Во-первых, поменяем models.py
еще раз:
Python
from django.db import models # Добавление новых импортов для ClusterTaggableManager, TaggedItemBase, MultiFieldPanel from modelcluster.fields import ParentalKey from modelcluster.contrib.taggit import ClusterTaggableManager from taggit.models import TaggedItemBase from wagtail.core.models import Page, Orderable from wagtail.core.fields import RichTextField from wagtail. admin.edit_handlers import FieldPanel, InlinePanel, MultiFieldPanel from wagtail.images.edit_handlers import ImageChooserPanel from wagtail.search import index # … (Сохраняем определение BlogIndexPage) class BlogPageTag(TaggedItemBase): content_object = ParentalKey( ‘BlogPage’, related_name=’tagged_items’, on_delete=models.CASCADE ) class BlogPage(Page): date = models.DateField(«Post date») intro = models.CharField(max_length=250) body = RichTextField(blank=True) tags = ClusterTaggableManager(through=BlogPageTag, blank=True) # … (Сохраняем определение методов main_image и search_fields) content_panels = Page.content_panels + [ MultiFieldPanel([ FieldPanel(‘date’), FieldPanel(‘tags’), ], heading=»Blog information»), FieldPanel(‘intro’), FieldPanel(‘body’), InlinePanel(‘gallery_images’, label=»Gallery images»), ]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | from django. db import models
# Добавление новых импортов для ClusterTaggableManager, TaggedItemBase, MultiFieldPanel
from modelcluster.fields import ParentalKey from modelcluster.contrib.taggit import ClusterTaggableManager from taggit.models import TaggedItemBase
from wagtail.core.models import Page, Orderable from wagtail.core.fields import RichTextField from wagtail.admin.edit_handlers import FieldPanel, InlinePanel, MultiFieldPanel from wagtail.images.edit_handlers import ImageChooserPanel from wagtail.search import index
# … (Сохраняем определение BlogIndexPage)
class BlogPageTag(TaggedItemBase): content_object = ParentalKey( ‘BlogPage’, related_name=’tagged_items’, on_delete=models.CASCADE )
class BlogPage(Page): date = models.DateField(«Post date») intro = models.CharField(max_length=250) body = RichTextField(blank=True) tags = ClusterTaggableManager(through=BlogPageTag, blank=True)
# . .. (Сохраняем определение методов main_image и search_fields)
content_panels = Page.content_panels + [ MultiFieldPanel([ FieldPanel(‘date’), FieldPanel(‘tags’), ], heading=»Blog information»), FieldPanel(‘intro’), FieldPanel(‘body’), InlinePanel(‘gallery_images’, label=»Gallery images»), ] |
Запустите python manage.py makemigrations
и python manage.py migrate
.
Обратите внимание на новые импорты modelcluster
и taggit
, внесение новой модели BlogPageTag
и внесение поля тегов в BlogPage
. Мы также воспользовались возможность использовать MultiFieldPanel
в content_panels
для группировки данных и полей тегов вместе для читаемости.
Поменяйте один из экземпляров ваших BlogPage
, и вы сможете отмечать посты:
Для отображение тегов в BlogPage
, добавьте следующее в blog_page. html
:
{% if page.tags.all.count %} <div> <h4>Tags</h4> {% for tag in page.tags.all %} <a href=»{% slugurl ‘tags’ %}?tag={{ tag }}»><button type=»button»>{{ tag }}</button></a> {% endfor %} </div> {% endif %}
1 2 3 4 5 6 7 8 | {% if page.tags.all.count %} <div> <h4>Tags</h4> {% for tag in page.tags.all %} <a href=»{% slugurl ‘tags’ %}?tag={{ tag }}»><button type=»button»>{{ tag }}</button></a> {% endfor %} </div> {% endif %} |
Обратите внимание на то, что здесь мы ссылаемся на страницы при помощи встроенного тега slugurl
, вместо pageurl
, которым мы пользовались ранее. Разница в том, что slugurl
использует слаг Page
(из панели Promote) в качестве аргумента. В то же время, pageurl
чаще используется, так как он прямолинеен и избегает дополнительных поисков в базе данных. Но в случае с данным циклом, объект Page
не является доступным, так что нам понадобится менее предпочитаемый тег slugurl
.
Переход к тегам в постах блога теперь должно показывать набор связанных кнопок внизу — по одной на каждый тег. Однако, нажатие на кнопку выведет ошибку 404, так как мы еще не определили вид тегов. Нужно добавить следующее в models.py
:
Python
class BlogTagIndexPage(Page): def get_context(self, request): # Фильтр по тегам tag = request.GET.get(‘tag’) blogpages = BlogPage.objects.filter(tags__name=tag) # Обновление контекста шаблона context = super().get_context(request) context[‘blogpages’] = blogpages return context
1 2 3 4 5 6 7 8 9 10 11 12 | class BlogTagIndexPage(Page):
def get_context(self, request):
# Фильтр по тегам tag = request. GET.get(‘tag’) blogpages = BlogPage.objects.filter(tags__name=tag)
# Обновление контекста шаблона context = super().get_context(request) context[‘blogpages’] = blogpages return context |
Обратите внимание на то, что эта базируемая на Page
модель по умолчанию не определяет поля. Даже без полей, создание подкласса Page
делает его частью экосистемы Wagtail, так что вы можете дать ему заголовок и URL в админке, а также управлять его содержимым, возвращая QuerySet
из метода get_context()
.
Проведите миграцию, затем создайте BlogTagIndexPage
в админке. Вам возможно понадобится создать новую страницу или вид в качестве дочернего элемента домашней страницы, параллельно с главной страницей вашего блога (/blog
). Назначьте ему слаг “tags
” в панели Promote.
Перейдите к /tags
и Django скажет вам то, что вы скорее всего уже знаете: вам нужно создать шаблон blog/blog_tag_index_page. html
:
{% extends «base.html» %} {% load wagtailcore_tags %} {% block content %} {% if request.GET.tag|length %} <h5>Showing pages tagged «{{ request.GET.tag }}»</h5> {% endif %} {% for blogpage in blogpages %} <p> <strong><a href=»{% pageurl blogpage %}»>{{ blogpage.title }}</a></strong><br /> <small>Revised: {{ blogpage.latest_revision_created_at }}</small><br /> {% if blogpage.author %} <p>By {{ blogpage.author.profile }}</p> {% endif %} </p> {% empty %} No pages found with that tag. {% endfor %} {% endblock %}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | {% extends «base. html» %} {% load wagtailcore_tags %}
{% block content %}
{% if request.GET.tag|length %} <h5>Showing pages tagged «{{ request.GET.tag }}»</h5> {% endif %}
{% for blogpage in blogpages %}
<p> <strong><a href=»{% pageurl blogpage %}»>{{ blogpage.title }}</a></strong><br /> <small>Revised: {{ blogpage.latest_revision_created_at }}</small><br /> {% if blogpage.author %} <p>By {{ blogpage.author.profile }}</p> {% endif %} </p>
{% empty %} No pages found with that tag. {% endfor %}
{% endblock %} |
Мы вызываем встроенное поле latest_revision_created_at
в модели Page
— приятно знать, что оно всегда в доступе.
Мы еще не добавили поле “author” в нашу модель BlogPage
, как и модель профиля для авторов — оставим это как практическое задание для читателя.
Нажатие на кнопку тега внизу поста теперь должно открывать страницу следующим образом:
Категории
Давайте добавим систему категорий в наш блог. В отличие от тегов, где автор страницы может внести тег, просто используя его на странице, наши категории будут фиксированным списком, управляемым владельцем сайта из отдельной области интерфейса админки.
Сначала, мы определим модель BlogCategory
. Категория не является страницей сама по себе, так что мы определим ее как стандартную models.Model
в Django, вместо наследования из Page
. Wagtail предоставляет концепт “сниппетов” для используемых повторно частей контента, которыми нужно управлять из админки, но не существует как часть дерева сайта.
Модель может быть зарегистрирована путем внесения декоратора @register_snippet
. Все типы полей, которые мы использовали на данный момент на странице могут быть использованы в сниппетах — здесь мы дадим иконку каждой категории, а также название. Внесем в blog/models.py
следующее:
Python
from wagtail.snippets.models import register_snippet @register_snippet class BlogCategory(models.Model): name = models.CharField(max_length=255) icon = models.ForeignKey( ‘wagtailimages.Image’, null=True, blank=True, on_delete=models.SET_NULL, related_name=’+’ ) panels = [ FieldPanel(‘name’), ImageChooserPanel(‘icon’), ] def __str__(self): return self.name class Meta: verbose_name_plural = ‘blog categories’
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | from wagtail.snippets. models import register_snippet
@register_snippet class BlogCategory(models.Model): name = models.CharField(max_length=255) icon = models.ForeignKey( ‘wagtailimages.Image’, null=True, blank=True, on_delete=models.SET_NULL, related_name=’+’ )
panels = [ FieldPanel(‘name’), ImageChooserPanel(‘icon’), ]
def __str__(self): return self.name
class Meta: verbose_name_plural = ‘blog categories’ |
Обратите внимание: Мы используем
panels
вместоcontent_panels
в данном коде, так как сниппеты в целом не нуждаются в полях так, как слаги или дата публикации, интерфейс редактирования для них не делится на отдельные панели “контент” / “настройки” / “продвижение” в качестве стандартных, и здесь нет необходимости проводить различие между “информационными панелями” и “рекламными панелями”.
Выполните миграцию данного изменения, и создайте несколько категорий через область сниппетов, которая теперь должна появиться в меню админки.
Теперь мы можем добавлять категории в модель BlogPage
в качестве мульти-поля. Тип поля, который мы используем для этого — ParentalManyToManyField
. Это вариант стандартного поля ManyToManyField
в Django, которое проверяет, правильно ли хранятся выбранные объекты в записях истории изменений. Во многом это тот же способ, которым ParentalKey
заменяет ForeignKey
для отношений “один ко многим”.
Python
# Новые импорты вносятся для форм и ParentalManyToManyField from django import forms from django.db import models from modelcluster.fields import ParentalKey, ParentalManyToManyField from modelcluster.contrib.taggit import ClusterTaggableManager from taggit.models import TaggedItemBase # … class BlogPage(Page): date = models. DateField(«Post date») intro = models.CharField(max_length=250) body = RichTextField(blank=True) tags = ClusterTaggableManager(through=BlogPageTag, blank=True) categories = ParentalManyToManyField(‘blog.BlogCategory’, blank=True) # … (Сохраняем определение методов main_image и search_fields) content_panels = Page.content_panels + [ MultiFieldPanel([ FieldPanel(‘date’), FieldPanel(‘tags’), FieldPanel(‘categories’, widget=forms.CheckboxSelectMultiple), ], heading=»Blog information»), FieldPanel(‘intro’), FieldPanel(‘body’), InlinePanel(‘gallery_images’, label=»Gallery images»), ]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | # Новые импорты вносятся для форм и ParentalManyToManyField from django import forms from django. db import models
from modelcluster.fields import ParentalKey, ParentalManyToManyField from modelcluster.contrib.taggit import ClusterTaggableManager from taggit.models import TaggedItemBase
# …
class BlogPage(Page): date = models.DateField(«Post date») intro = models.CharField(max_length=250) body = RichTextField(blank=True) tags = ClusterTaggableManager(through=BlogPageTag, blank=True) categories = ParentalManyToManyField(‘blog.BlogCategory’, blank=True)
# … (Сохраняем определение методов main_image и search_fields)
content_panels = Page.content_panels + [ MultiFieldPanel([ FieldPanel(‘date’), FieldPanel(‘tags’), FieldPanel(‘categories’, widget=forms.CheckboxSelectMultiple), ], heading=»Blog information»), FieldPanel(‘intro’), FieldPanel(‘body’), InlinePanel(‘gallery_images’, label=»Gallery images»), ] |
Здесь мы используем аргумент widget
в определении FieldPanel
для спецификации виджета, основанного на чекбоксе, вместо стандартного бокса множественного выбора. Такой подход можно назвать наиболее удобным для пользователя.
Наконец, мы можем обновить шаблон blog_page.html
для отображения категорий:
<h2>{{ page.title }}</h2> <p>{{ page.date }}</p> {% with categories=page.categories.all %} {% if categories %} <h4>Posted in:</h4> <ul> {% for category in categories %} <li> {% image category.icon fill-32×32 %} {{ category.name }} </li> {% endfor %} </ul> {% endif %} {% endwith %}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <h2>{{ page.title }}</h2> <p>{{ page.date }}</p>
{% with categories=page.categories.all %} {% if categories %} <h4>Posted in:</h4> <ul> {% for category in categories %} <li> {% image category. icon fill-32×32 %} {{ category.name }} </li> {% endfor %} </ul> {% endif %} {% endwith %} |
Vasile Buldumac
Являюсь администратором нескольких порталов по обучению языков программирования Python, Golang и Kotlin. В составе небольшой команды единомышленников, мы занимаемся популяризацией языков программирования на русскоязычную аудиторию. Большая часть статей была адаптирована нами на русский язык и распространяется бесплатно.
E-mail: [email protected]
Образование
Universitatea Tehnică a Moldovei (utm.md)
- 2014 — 2018 Технический Университет Молдовы, ИТ-Инженер. Тема дипломной работы «Автоматизация покупки и продажи криптовалюты используя технический анализ»
- 2018 — 2020 Технический Университет Молдовы, Магистр, Магистерская диссертация «Идентификация человека в киберпространстве по фотографии лица»
Запускаем простой блог на Wagtail CMS (Django)
Являясь большим фанатом Python и фреймворка Django постоянно искал решение, как сделать разработку новых веб-проектов быстрее и удобнее.
Все, кто знаком с разработкой на Django, знают насколько неудобно строить на нем интуитивно понятную админ.панель. До мегапопулярного WordPress очень далеко, что делает порог вхождения в разработку сайтов выше, чем у PHP-фреймворков и CMS.
После долгого поиска и тестирования различных решений я нашел для себя оптимальный вариант — Wagtail CMS.
Wagtail — это полноценная CMS написанная на Django компанией Torchbox. За что им большое спасибо. Проект с открытым исходным кодом, поддерживается сообществом энтузиастов и выпускается под BSD лицензией.
Вот типичный интерфейс Wagtail:
В wagtail очень удобно работать с контентом, создавать новые публикации, разделы, работать с изображениями. Внутри этого проекта много интересных технологий. Но сегодня не об этом.
Расскажу пример по быстрому запуску простого блога для новичков в Django разработке, с учетом многих трудностей, которые могут поджидать на пути.
Нам понадобится:
1. VPS минимум с 1gb памяти
2. Базовое знакомство с django
3. Навыки в удаленной настройке Ubuntu
Шаг 1:
Вы запустили VPS сервер для разработки. Используйте Putty для удаленного подключения по SSH.
Настройка проводиться на сервере с ОС Ubuntu 14.04.
— Логинимся и начинаем базовую настройку:
sudo apt-get update sudo apt-get upgrade
Как правило, на сервере уже стоит Python 2 и 3 версии. Проверяем следующими командами:
Python -V Python3 -V
Для проекта будем использовать Python 3, так как с ним лучше работает Pillow и некоторые библиотеки, которые нам понадобятся.
Если каким-то образом у вас на сервере не оказалось python3, то ставим его командой:
sudo apt-get install python3
Так же нам потребуется python3-setuptools и python3-dev:
sudo apt-get install python3-setuptools sudo apt-get install python3-dev
Учитывая, что все ставиться на чистую систему поставим PIP (чтобы поставить свежую версию »8. 1.2» лучше делать это через easy_install3):
sudo easy_install3 pip
Проверить версию PIP можно командой:
pip -V
Если версия не самая свежая, то обновляем pip командой:
sudo pip install --upgrade pip
Чтобы в дальнейшем у нас не выпадало различных ошибок при установке Wagtail, сразу ставим все необходимые библиотеки для Pillow:
sudo apt-get install libtiff5-dev libjpeg8-dev zlib1g-dev \ libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev python-tk
Теперь нам потребуется виртуальное окружение для проекта, ставим virtualenv
sudo pip3 install virtualenv
Создаем виртуалку:
virtualenv /opt/myenv
Идем в папку /opt/myenv и запускаем виртуальную среду:
source bin/activate
Вы должны увидеть строку с названием (myenv), означающую, что виртуалка запущена:
(myenv) [email protected]:/opt/myenv$
Шаг 2: Предварительная настройка сделана, переходим к установке Wagtail CMS и настройке базы данных:
1. ставим Wagtail и Gunicorn:
sudo pip3 install wagtail gunicorn
2. Создаем наш будущий блог командой
wagtail start mysite
3. Перейдем в папку блога (mysite) и установим все зависимости для проекта из файла requirements.txt
cd mysite pip install -r requirements.txt
4. На данном этапе вы можете провести миграции и сразу запустить тестовый сервер вашего будущего блога. Но так как мы планируем сделать все правильно, то наш блог будет работать на PostgreSQL (Для django можно использовать разные базы данных, в том числе MySQL, PostgreSQL, Oracle DB и т.д). Так что проводим настройку базы данных:
sudo apt-get install libpq-dev sudo apt-get install postgresql postgresql-contrib
5. На данный момент у вас уже запущен postgresql на порту 5432. Но чтобы база данных могла взаимодействовать с Wagtail нужно поставить psycopg2:
pip install psycopg2
6. Теперь надо создать саму базу данных для проекта, для этого зайдем под пользователем postgres и проведем некоторые дополнительные настройки:
sudo su – postgres createdb myblog
7. Так как postgresql создает пользователя не спрашивая вас про пароль, нам надо назначит ему пароль через psql:
psql
Теперь вы можете выполнить команду смены пароля:
\password postgres
Выходим из psql и закрываем сеанс под пользователем postgres:
\q exit
Проверьте, что вы еще в виртуально среде, если нет то опять активируйте виртуалку.
8. Теперь нам надо настроить Wagtail на взаимодействие c postgresql, для этого идем в base.py:
cd /opt/myenv/mysite/mysite/settings/ nano base.py
Открывается файл настроек, нам нужна запись DATABASES. Удаляем, то что есть и ставим туда следующее: DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2', # указываем, что база данных postgresql
'NAME': 'myblog', # Название вашей базы данных
'USER': 'postgres',
'PASSWORD': '******',
'HOST': 'localhost',
'PORT': '',
}
}
Кстати, в wagtail есть русский язык, так что можете заодно в настройках поправить параметр LANGUAGE_CODE. LANGUAGE_CODE = 'ru-ru'
Собственно, мы провели все необходимые настройки, и теперь можно запускать миграции.
Шаг 3. Первый запуск
Перейдите в корневую папку вашего блога (cd /opt/myenv/mysite/) и начните вашу первую миграцию данных в данном проекте:
python manage.py migrate
Теперь создайте пользователя под которым вы будете заходить в админ.панель Wagtail:
python manage.py createsuperuser
Введите имя (например admin), пароль и почту.
Проверяем, что все работает:
python manage.py runserver 0.0.0.0:8000
Перейдем в браузере по ip адресу вашего VPS на 8000 порт (пример 199.199.32.32:8000) и увидим стартовую страницу wagtail.
Отлично, теперь перейдем в админ панель 199.199.32.32:8000/admin, вводим логин и пароль пользователя которые мы создали ранее и попадаем в удобный интерфейс Wagtail CMS:
Пока что наш сайт ничего не умеет, но скоро мы научим его всему необходимому для блога.
Шаг 4. Настройка стартовой страницы сайта
За главную страницу отвечает приложение «home» которое запускается из коробки. Чтобы стартовая страница была такой как вам надо внесем некоторые изменения в код.
Структура вашего сайта имеет такой вид:
mysite/
home/
migrations/
__init__.py
0001_initial.py
0002_create_homepage.py
templates/
home/
home_page.html
__init__.py
models.py
search/
templates/
search/
search.html
__init__.py
views.py
mysite/
settings/
__init__.py
base.py
dev.py
production.py
static/
css/
mysite.css
js/
mysite.js
templates/
404.html
500.html
base.html
__init__.py
urls.py
wsgi.py
manage.py
requirements.txt
Отредактируйте файл «home/models.py» следующим образом:
from __future__ import unicode_literals from django.db import models from wagtail.wagtailcore. models import Page from wagtail.wagtailcore.fields import RichTextField from wagtail.wagtailadmin.edit_handlers import FieldPanel class HomePage(Page): body = RichTextField(blank=True) content_panels = Page.content_panels + [ FieldPanel('body', classname="full") ]
Также отредактируйте файл home/templates/home/home_page.html: {% extends "base.html" %}
{% load wagtailcore_tags %}
{% block body_class %} template-homepage {% endblock %}
{% block content %}
{{ page.body | richtext }}
{% endblock %}
Теперь, чтобы новые изменения заработали, проведите миграции:
python manage.py makemigrations python manage.py migrate
После этого вы можете зайти в админ.панель и отредактировать текст на вашей стартовой странице.
Сохраните изменения и откройте опять ваш сайт. Вы увидите свой текст, примерно такого вида:
Для начала неплохо, но надо добавить разметку и красоту css.
За базовый шаблон отвечает файл «base. html» расположенный в mysite/settings/base.py;
За css отвечает «mysite.css» расположенный в mysite/static/css/mysite.css;
За js отвечает «mysite.js» расположенный в mysite/static/js/mysite.js;
Я поклонник materializecss и для нашего сайта мы возьмем у них шаблон и библиотеки.
Смотреть тут http://materializecss.com/getting-started.html
Вносим следующие изменения в base.html:
{% load static wagtailuserbar %} {% block title %}{% if self.seo_title %}{{ self.seo_title }}{% else %}{{ self.title }}{% endif %}{% endblock %}{% block title_suffix %}{% endblock %} {# Global stylesheets #} {% block extra_css %} {# Override this in templates to add extra stylesheets #} {% endblock %} {% wagtailuserbar %}{% block content %}{% endblock %}
{# Global javascript #} {% block extra_js %} {# Override this in templates to add extra javascript #} {% endblock %}
Сохраняем и снова открываем наш сайт. Должно получиться, что-то похожее на эту картинку:
Уже лучше. Теперь нам надо создать блог, ради которого все и затевалось.
Шаг 5. Создание блога.
Идем опять в корень нашего проекта и выполняем следующую команду:
python manage.py startapp blog
После этого добавьте новое приложение «blog» в INSTALLED_APPS в mysite/settings/base.py.
Для каждой записи нашего блога будут стандартные поля — заголовок, дата, картинка, введение и основной текст. Чтобы все это работало внесите следующие изменения в «blog/models.py»
from django.db import models from wagtail.wagtailcore.models import Page from wagtail.wagtailcore.fields import RichTextField from wagtail.wagtailadmin.edit_handlers import FieldPanel from wagtail.wagtailimages.edit_handlers import ImageChooserPanel from wagtail.wagtailsearch import index class BlogPage(Page): main_image = models.ForeignKey( 'wagtailimages.Image', null=True, blank=True, on_delete=models. SET_NULL, related_name='+' ) date = models.DateField("Post date") intro = models.CharField(max_length=250) body = RichTextField(blank=True) search_fields = Page.search_fields + [ index.SearchField('intro'), index.SearchField('body'), ] content_panels = Page.content_panels + [ FieldPanel('date'), ImageChooserPanel('main_image'), FieldPanel('intro'), FieldPanel('body'), ]
Теперь создайте саму страницу, на которую все это будет выводиться в blog/templates/blog/blog_page.html:
{% extends "base.html" %}
{% load wagtailcore_tags wagtailimages_tags %}
{% block body_class %}template-blogpage{% endblock %}
{% block content %}
{{ page.date }}
{% if page.main_image %}
{% image page.main_image width-500 %}
{% endif %}
{{ page.intro }}
{{ page.body|richtext }}
{% endblock %}
Пока что ничего не работает, поэтому выполняем команды
python manage. py makemigrations python manage.py migrate
Теперь можно создать первую запись в нашем блоге, через удобный интерфейс wagtail.
Идем в админ.панель, открываем «Проводник», выбираем главную, и нажимаем «добавить дочернюю страницу», появиться следующее окно с выбором шаблонов для создаваемой страницы:
Выбираем «Blog Page» и заполняем страницу информацией.
Вот пример редактирования страницы блога в админ.панели wagtail:
Довольно удобно и быстро.
Сохраняем запись и открываем в браузере. Первая запись вашего блога готова.
Если хоть кому-то данный пост окажется полезным, то я продолжу публикацию постов на тему wagtail, в которой расскажу подробнее про различные возможности данной CMS.
Для всех заинтересовавшихся Wagtail CMS:
Официальный сайт https://wagtail.io/
Гитхаб проекта https://github.com/torchbox/wagtail/
Документация http://docs.wagtail.io/en/latest/index.html
Демосайт для изучения http://docs.wagtail.io/en/latest/getting_started/demo_site. html
Группа поддержки пользователей https://groups.google.com/forum/#! forum/wagtail
© Habrahabr.ru
Wagtail on Azure Linux VM · Microsoft Azure
Веб-приложение на Python, virtualenv, Wagtail (Django), Gunicorn (WSGI HTTP Server), PostgreSQL (pip psycopg2). С примером веб-приложения на Wagtail можно ознакомиться по ссылке.
Для установки Wagtail CMS на Linux Debian можно воспользоваться данной инструкцией:
- Обновить зависимости и пакеты:
sudo apt-get update yes | sudo apt-get dist-upgrade
- Проверить доступные версии Python и при необходимости установить Python3:
python -V # проверка версии python python3 -V # проверка версии python3 yes | sudo apt-get install python3 # установка python3, если отсутствует
- Установить дополнительные утилиты python/pip:
yes | sudo apt-get install python3-setuptools python3-dev # установка easy_install и python3-dev, если отсутствуют sudo easy_install pip # установка pip, если отсутствует pip -V # просмотр версии pip sudo pip install --upgrade pip # обновление pip при необходимости
- Установить дополнительных библиотек:
yes | sudo apt-get install libtiff5-dev libjpeg9-dev zlib1g-dev libfreetype6-dev liblcms2-dev libwebp-dev tcl8. 6-dev tk8.6-dev python-tk gcc libc-dev
- Установка и активация виртуального окружения:
sudo pip3 install virtualenv sudo virtualenv /opt/artenv cd /opt/artenv source bin/activate
Должно появится приглашение на подобие следующего:
(myenv) [email protected]:/opt/artenv$
- Установка Wagtail и Gunicorn:
sudo pip3 install wagtail gunicorn wagtail start artblog # создание каркаса нашего приложение
- Перейти в основную папку проекта /opt/artenv/artblog и установить зависимости:
sudo pip install -r requirements.txt
- Установить и настроить базу данных проекта (PostgreSQL):
sudo apt-get install libpq-dev postgresql postgresql-contrib ps -ef | grep postgre # проверим работу базы данных # стандартный порт 5432 sudo pip install psycopg2 # установим утилиту для взаимодействия нашего приложения с базой данных sudo su - postgres # заходим под стандартным пользователем postgresql psql # запускаем консольную утилиту для связи с базой данных # вводим запрос для создания базы данных и пользователя CREATE DATABASE <dbname> with encoding='UNICODE'; CREATE USER <username> with password '<dbpassword>'; GRANT ALL PRIVILEGES ON DATABASE <dbname> TO <username>; # выходим из psql и завершаем сеанс пользователя postgres \q exit # проверяем, что мы всё еще в виртуальной среде # подключаем наше приложение к базе данных sudo nano /opt/artenv/artblog/artblog/settings/base. py # ищем запись DATABASES, и вносим указанные ранее значения DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql_psycopg2', 'NAME': '<dbname>', 'USER': '<username>', 'PASSWORD': '<dbpassword>', 'HOST': 'localhost', 'PORT': '', } } # при необходимости в том же файле можно установить русский язык LANGUAGE_CODE = 'ru-ru' # после чего закрываем редактор, сохраняя изменения: нажимаем ctrl+x, вводим y, нажимаем enter
- Изменим первую строку в файле manage.py для того, чтобы она указывала на python в нашем виртуальном окружении и выполним миграцию:
sudo nano /opt/artenv/artblog/manage.py # откроем в редакторе nano необходимый файл # установим в первой строке запись, указывающую на python в виртуальном окружении, например, так #!/opt/artenv/bin/python cd /opt/artenv/artblog # перейдем в папку с нашим проектом sudo python manage.py migrate # выполним миграцию # если появилась ошибка, что не установлен модуль django, то пробуем следующую команду, связанную с вашей версией python, например, в случае python3. 6: sudo python3.6 manage.py migrate # далее используем вместо python нашу версию python3.x
- Создаем профиль администратора и запускаем наше приложение:
cd /opt/artenv/artblog # переходим в директорию нашего проекта sudo python manage.py createsuperuser # создаем администратора нашего приложения, вводим логин, email, пароль deactivate # отключим наше виртуальное окружение python3.x manage.py runserver 0.0.0.0:8000 # запускаем приложение на 8000 порту для проверки и отладки # если есть запрос на измнение ALLOWED_HOSTS, то делаем следующее # добавляем наш хост в соответствующий пункт в settings.py sudo nano /usr/local/lib/python3.x/dist-packages/django/conf/global_settings.py # в качестве примера # добавляем наши хосты в ALLOWED_HOSTS = ['localhost','127.0.0.1','domain.com'] и т.п. python3.x manage.py runserver 0.0.0.0:80 # запускаем наше приложение на 80-м порту
- Если мы работаем на сервере, например, через ssh клиент (PuTTY или другой), то возможно нам понадобится запустить приложение через nohup с установкой процесса в качестве фонового (в конце команды &), чтобы веб-приложение продолжило свою работу после закрытия ssh-клиента:
cd /opt/artenv/artblog # переходим в директорию нашего проекта nohup sudo python3. x manage.py runserver 0.0.0.0:80 & # запускаем приложение в виде фонового процесса (задания) jobs -p # используем для просмотра наших заданий и их PID ps -ef # также можно просмотреть все процессы (если мы перелогинились до этого) sudo kill PID # для отключения нашего приложения используем данную команду (PID находим при помощи команд, указанных выше
- Для удобной работы в консоли Linux Debian возможно понадобятся следующие команды:
history # история введенных нами команд (зависит от пользователя, под которым мы находимся) !x # x - номер команды из истории, позволяет выполнить определенную команду, введенную ранее !! # выполнить предыдущую команду sudo !! # выполнить предыдущую команду от имени администратора
Полезные ссылки:
- Python3 | Docs
- PostgreSQL | Docs
- Python Package Index | pip
- Django | Docs
- Wagtail CMS | Docs
- Markdown 2.6.11 | Python implementation of Markdown
- Django-el-pagination | Django pagination tools
- Django-taggit | Reusable Django application for simple tagging
- GitHub Guides | Mastering Markdown
Wagtail — лучшая CMS на python в нашей галактике
Wagtail — это популярная CMS на основе Django. Django предоставляет огромные возможности — с его помощью созданы не только сайты, но и, например, популярный веб-сервис комментариев disqus, веб-сервис хранения git репозиториев bitbucket и многое другое. На django можно создать все что угодно.
Wagtail предоставляет пользователям новые, построенные с помощью Django, абстракции. Можно провести следующую аналогию: если django предоставляет вам молоток, пилу, гвозди и деревянные доски, то Wagtail сразу дает готовые столы, стулья и прочую мебель сколоченную из того, что есть в django. Для своего проекта вы конечно можете собрать необходимую мебель сами, используя только django, но во многих случаях будет гораздо проще и быстрее взять что-то готовое из Wagtail.
Из комментариев в интернет кажется, что многие считают Wagtail надстройкой к админке django, но это далеко не так. В этой статье я расскажу о Wagtail, которую считаю лучшей CMS и приведу некоторые абстракции, предоставляемые Wagtail.
Кто стоит за Wagtail
История Wagtail начинается с 2014 года, когда британская компания Torchbox разработала сайт для Королевского колледжа искусств. Если пройти на сайт с установленным расширением Wappalyzer, можно убедиться, что заведение по сей день использует Wagtail. Изначально планировалось, что Wagtail станет open source проектом, каким он и остается по сей день. Torchbox ведет разработку своей CMS по настоящее время и предлагает платные услуги по поддержанию и созданию сайтов на Wagtail.
Скрин Wappalyzer с сайта Королевского колледжа искусствСейчас Wagtail используют такие организации как Mozilla Foundation, Nasa, Британский минздрав, Калтех, google в официальном туториале для своей облачной платформы устанавливает Wagtail на App Engine и многие другие. Если пройти по ссылкам из предыдущего предложения, опять же можно проверить Wappalyzer или любым другим инструментом, что сайты действительно используют Wagtail CMS.
На момент написания статьи, гитхаб репозиторий Wagtail имеет чуть больше 11 тысяч звезд и в разработке успело поучаствовать 496 человек, репозиторий очень активный. Для сравнения репозиторий Django CMS — еще одной CMS написанной с использованием Django имеет почти 8. 5 тыс звезд и 440 участников а репозиторий WordPress имеет 15.5 тыс звезд и 63 участника. Команда Wagtail регулярно выпускает новые версии по заданному расписанию, например следующая версия 2.15 ожидается 1 ноября 2021.
Основные идеи Wagtail
Разработчик подошли к созданию Wagtail с некоторыми ключевыми идеями в голове, которые они назвали «Дзен Wagtail» по аналогии с «Дзен Питона». Вот мой пересказ Дзена Wagtail:
Во-первых на Wagtail нельзя создать сайт без программирования. Если вам нужно что-то простое и быстро, лучше не тратить время на Wagtail — не забивайте гвозди микроскопом, установите готовую CMS и настройте ее мышкой. Wagtail актуален для создания проектов, которые могут иметь много абстракций, сложные зависимости между ними, несколько пользователей с различными ролями и т.п.
Создание и поддержание сайта это общая работа нескольких людей. В Wagtail удобно и необходимо разделять обязанности разработчика, дизайнера, администратора сайта и автора, подготавливающего материалы на сайт.
Авторы материалов для сайта должны иметь удобный и быстрый способ добавлять свои идеи/мысли/новости на сайт. Авторы не должны писать код на css или html. Дизайн и контент должен быть отдельно.
Лучшим инструментом для разработчика сайта является язык программирования. Wagtail предполагает, что разработчик сам напишет какие-то нужные для сайта дополнения.
Некоторые возможности Wagtail
Страницы
Одной из важных абстракций, предоставляемых Wagtail является страница — Page. Страница предоставляет кучу полезного функционала из коробки — черновик, можно сохранять все версии страницы в базе данных (по мере их редактирования), дата публикации, дата изменения и много другое. Можно создать сколько угодно отдельных типов страниц, наследуя от Page. Например, отдельные классы для страниц блога, новостей, сотрудников и т.п. В каждом из таких типов можно задать, какой контент должен быть на странице — например, просто текст, изображения, видео. .. или все вместе.
Страницы организованы в виде графа — каждая страница может быть дочерней или родительской к другой странице, что можно указать в коде. Например, можно сделать страницу «Люди» /people/, в которой будет написано какой классный коллектив работает в компании. Дочерними страницами для страницы «Люди» можно сделать индивидуальные страницы сотрудников например /people/ceo, /people/cto… Все это делается в Wagtail в пару строк кода.
Управлять содержимым страниц можно только из админ панели wagtail.
Пример редактирования страницы WagtailStreamField
Еще одним важным инструментом является StreamField. StreamField позволяет создать контент из блоков — также как, например, пишутся статьи на хабре, vc, vk — короче это уже стало стандартом. Ваша страница это просто набор блоков — блок изображение с подписью, блок текст, блок видео… В Wagtail можно создать собственные виды блоков. Например, можно создать блок для карусели картинок, как в vk.
Картинки
Wagtail дает возможность обрезать картинки как угодно, в интерфейсе можно указать фокусную точку на картинке и Wagtail будет стремиться показать ее на страницах с изображением. В документации есть также инструкция как настроить Wagtail на распознавание отдельных элементов на картинках (например лиц, кошечек, машинок и т.п.) и в дальнейшем обрезать картинки, чтобы распознанная часть попадала куда нужно.
API
Нужна безголовая (Headless) CMS? Нет проблем! REST API в Wagtail доступно практически по умолчанию (пара строк кода, скопированных из документации). Если нужно что-то круче — graphql — есть пост от Torchbox с кодом и инструкциями какие пакеты нужно установить.
Другие возможности
Админ интерфейс Wagtail легко настраивается — можно добавить новые элементы меню, поменять логотип, поменять цвета. Админ интерфейс адаптивный — нормально выглядит на смартфоне, компьютере, планшете и т.п. Еще в Wagtail легко создавать сложные таблицы, из коробки доступен ставший стандартом elasticsearch, есть разделение доступа между пользователями а также группами пользователей и многое другое.
Освоить Wagtail
Предположу, что написанное выше звучит очень абстрактно и чтобы разобраться нужно посмотреть примеры и пописать код.
У Wagtail отличная документация, хотя на ответах со stackoverflow я не раз сталкивался с примерами недокументированных возможностей. Кстати на stackoverflow у Wagtail отличная поддержка. На один мой вопрос даже ответил сотрудник Torchbox Matt Westcott — один из ключевых разработчиков Wagtail.
Отличные уроки по Wagtail на английском подготовил Kalob Taulien:
Доступных материалов на русском, к сожалению, не так много. Все, что я смог найти это:
На хабре есть один старенький туториал из трех частей по запуску CMS (раз, два, три). Возможно, что-то оттуда уже устарело, но, попробовать запустить можно.
Туториал https://python-scripts.com/wagtail — надеюсь, автор написал тот пост не ради рефералки на хостинг.
Пользуясь случаем, не могу не прорекламировать блестящие уроки на youtube, отснятые моей скромной персоной:
Заключение
Видя какой путь прошел Wagtail, и как он развивается сейчас, уверен, что данная CMS будет только процветать, ведь ее поддерживают настоящие профи, которые зарабатывают деньги, делая Wagtail лучше, ей пользуются самые передовые организации и IT гиганты, в ней есть крутые возможности из коробки.
Установка и настройка Django CMS на сервере Debian 7 и Ubuntu 13
16 апреля, 2014 1:28 пп 4 135 views | Комментариев нетDebian, Linux, Python, Ubuntu, VPS, Без категорий | Amber |
Вступление
Django CMS – система управления контентом, разработанная для веб-приложений на основе фреймворка Django. Сегодня из трех десятков других доступных вариантов данная система, пожалуй, является самой популярной
Безусловно, эта зрелая, готовая к производству CMS, которой доверяют многие профессионалы и известные бренды со всего мира – великолепный выбор для разработки проектов.
Данная статья описывает установку и настройку Django CMS на Debian 7 и Ubuntu 13 VPS, а также подготовку данной системы к использованию. Если вы заинтересованы в работе с Django [CMS], но не имеете навыков использования фреймворков или языка программирования Python, не стоит беспокоиться: с Django CMS поставляется относительно простая и довольно информативная документация по разработке, с которой будет полезно ознакомиться после прочтения этого руководства. Последнюю версию документации можно найти по этой ссылке.
Pip: менеджер пакетов Python
Pip – менеджер пакетов, помогающий установить необходимые программные пакеты (инструменты, библиотеки, приложения и др.).
Setuptools: утилита распространение пакета Python
Библиотека setuptools, единственная зависимость pip, основана на функциональности набора стандартных утилит распространения программного обеспечения языка Python под названием distutils.
Virtualenv: построитель виртуальной среды Python
В Python многие задачи выполняются очень легко, и установка пакетов и приложений – не исключение. Тем не менее, значительное количество этих пакетов поставляется в зависимости от других пакетов. При инсталляции они становятся общесистемными – то есть, любое приложение Python может подключаться к этим библиотекам и использовать их.
При определенных обстоятельствах это может привести к серьезным сбоям уже настроенных и стабильно работающих приложений. Любой установленный или удаленный пакет так или иначе влияет на всю систему; следовательно, неверная версия библиотеки или модуля может стать причиной общесистемных повреждений. Потому часто на начальном этапе разработки необходима чистая рабочая среда.
Именно для этого существует инструмент virtualenv; с его помощью можно отделить репозиторий приложения Django CMS и его сложные зависимости от всей системы, что одновременно помогает поддерживать порядок в системе и облегчает техническое обслуживание.
Установка Django CMS
Процесс установки состоит из пяти действий.
1: Подготовка операционной системы
Примечание: Данное руководство использует новый, чистый VPS. Это помогает избежать возникновения связанных с ПО или безопасностью проблем.
Для начала нужно обновить систему. Обновите список программного обеспечения репозитория, а затем – установленные на машине инструменты до более актуальных версий:
aptitude update
aptitude upgrade
Теперь можно приступить к установке других необходимых программных средств и библиотек, а именно:
- python-dev: этот пакет расширяет установку Python по умолчанию на систему.
- libjpeg-dev/libpng-dev: эти библиотеки будут необходимы для обработки изображений с PIL.
- libpq-dev: версия libpq (PostgreSQL), которая понадобится при разработке.
Чтобы скачать и установить их, запустите:
aptitude install libpq-dev python-dev libjpeg-dev libpng-dev
2: Установка виртуальной среды
Все нужные пакеты работают на Python. Установка Debian 7 по умолчанию поставляется с версией Python 2.7, которая отвечает требованиям разработки. Потому можно перейти к инсталляции pip, который понадобится для установки virtualenv (и других пакетов).
Прежде чем установить pip, необходимо установить его зависимость – setuptools.
2.1: Установка setuptools
Загрузить установочные файлы setuptools можно с помощью инструмента под названием curl. Данные файлы не только автоматизируют процесс установки, но и предоставят последнюю версию необходимой программы. На данном этапе curl проверит SSL-сертификаты из исходного кода и передаст данные интерпретатору Python.
Выполните следующую команду:
$ curl https://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.py | python -
Это позволит установить setuptools в рамках всей системы.
Теперь можно установить и настроить pip.
2.2: Установка pip
Для загрузки и установки инструмента снова используйте curl. Запустите следующее:
$ curl https://raw.github.com/pypa/pip/master/contrib/get-pip.py | python -
Это позволит установить pip общесистемно.
По умолчанию pip устанавливает файлы в /usr/local/bin. Этот путь нужно внести в PATH, чтобы иметь возможность запускать данный инструмент с помощью команды pip. Итак, выполните:
export PATH="/usr/local/bin:$PATH"
После инсталляции менеджера пакетов pip установка всех остальных пакетов сводится к одной строке: pip install имя_пакета. Тем не менее, чтобы получить последнюю версию virtualenv, нужно указать pip ее адрес.
2.3: Установка virtualenv
Чтобы pip установил virtualenv, запустите следующее:
pip install https://github. com/pypa/virtualenv/tarball/1.9.X
Это установит virtualenv общесистемно.
Стандартная установка virtualenv имеет следующий вид:
pip install virtualenv
Это также установит virtualenv в рамках всей системы.
3: Подготовка виртуальной среды (venv) для Django CMS
Теперь все необходимые инструменты готовы, можно переходить к подготовке виртуальной среды для хранения Django CMS.
Для начала нужно инициировать venv (virtual environment) под названием django_cms с помощью инструмента virtualenv и перйти в папку проекта:
virtualenv django_cms
cd django_cms
В данном случае имя папки репозитория проекта – django_cms, но можно, конечно, установить любое имя. Имейте в виду, имя, никак не связанное с проектом, в будущем может привести к проблемам с обслуживанием.
Для использования виртуальной среды необходимо ее активировать.
source bin/activate
Для дезактивации среды используется команда deactivate.
4: Установка зависимостей Django CMS
4.1: Установка pillow (замена PIL)
Одна из необходимых зависимостей – это библиотека Python Imaging Library (или PIL), которая используется Django CMS (вместе с другими библиотеками, установленными ранее) для обработки изображений.
Но вместо PIL иногда лучше использовать более удобное ответвление данной библиотеки под названием Pillow. Данный пакет совместим с setuptools и автоматически устраняет некоторые проблемы, связанные с использованием PIL в виртуальной среде.
Чтобы скачать и установить Pillow, запустите:
django_cms$ pip install pillow
Поскольку виртуальная среда была активирована, Pillow не будет установлена общесистемно.
4.2: Установка драйверов базы данных
Django [CMS] позволяет выбрать несколько процессоров базы данных для питания приложения, а именно PostgreSQL, MySQL, Oracle и SQLite. Проект Django рекомендует использовать PostgreSQL (для этого необходимо установить некоторые библиотеки и драйвера, которые позволят использовать PostgreSQL в качестве внутреннего интерфейса приложения).
Адаптер базы данных PostgreSQL, который использует Django, называется psycopg2. Для его работы нужна библиотека libpq-dev (была установлена в начале руководства). Поэтому можно просто выполнить следующую команду для установки psycopg2 в venv:
django_cms$ pip install psycopg2
Поскольку venv активирована, psycopg2 не будет установлен общесистемно.
Примечание: данная команда подготавливает PostgreSQL для Django, но не выполняет полностью настроенной установки. Чтобы получить инструкции по дальнейшей работе PostgreSQL на Django, читайте данную статью.
Далее используется база данных SQLite. Для дальнейшей работы с PostgreSQL, пожалуйста, установите данный параметр.
5: Установка и настройка Django CMS внутри виртуальной среды Python
5.1: Установка Django CMS
Django CMS поставляется с рядом других зависимостей, которые еще необходимо установить. Тем не менее, благодаря pip оставшиеся зависимости можно установить и настроить автоматически с помощью пакета Django CMS: django-cms.
Для завершения установки просто запустите следующую команду:
django_cms$ pip install django-cms
Поскольку виртуальная среда была активирована, данный пакет не будет установлен общесистемно.
Теперь все необходимые компоненты установлены: Django, django-classy-tags, south, html5lib, django-mptt, django-sekizai.
5.2: Настройка Django CMS
Создание проекта Django CMS состоит из двух этапов. Сначала нужно создать обычный проект Django в виртуальной среде, а затем перейти к его настройке, чтобы он работал как проект Django CMS.
Итак, создайте проект Django. Назовем его dcms (но, конечно, можно выбрать абсолютно любое имя).
Просто запустите:
django_cms$ django-admin.py startproject dcms
django_cms$ cd dcms
Это создаст проект. Чтобы протестировать инсталляцию, прежде чем перейти к настройке проекта, запустите следующее (это действие запустит простой сервер разработки, к которому можно получить доступ извне):
django_cms$ python manage. py runserver 0.0.0.0:8000
Теперь посетите URL в браузере, заменяя 0.0.0.0 реальным IP-адресом.
Для завершения процесса настройки следуйте данным инструкциям.
5.3: Завершение настройки в соответствии с вводным руководством
Большинство конфигураций Django CMS находится в файле settings.py, расположенном в папке проекта.
Откройте данный файл в предпочитаемом текстовом редакторе. В данном руководстве используется nano.
django_cms$ nano dcms/settings.py
Внесите в начало файла следующие строки:
# -*- coding: utf-8 -*-
import os
gettext = lambda s: s
PROJECT_PATH = os.path.split(os.path.abspath(os.path.dirname(__file__)))[0]
Для начала найдите раздел INSTALLED_APPS. В данном разделе под списком уже существующих модулей нужно внести имена недавно установленных приложений, включая модуль Django CMS.
Как говорится в Django CMS:
Добавьте следующие приложения в INSTALLED_APPS. Данный список содержит django CMS, а также зависимости данной системы и другие рекомендованные приложения/библиотеки:
'cms', # django CMS itself
'mptt', # utilities for implementing a modified pre-order traversal tree
'menus', # helper for model independent hierarchical website navigation
'south', # intelligent schema and data migrations
'sekizai', # for javascript and css management
Примечание: прежде чем двигаться дальше, не забудьте раскомментировать django.contrib.admin. Данный модуль необходим для процедуры установки.
Затем найдите MIDDLEWARE_CLASSES и внесите следующие строки в конец раздела:
'cms.middleware.page.CurrentPageMiddleware',
'cms.middleware.user.CurrentUserMiddleware',
'cms.middleware.toolbar.ToolbarMiddleware',
'cms.middleware.language.LanguageCookieMiddleware',
После этого данный раздел должен выглядеть примерно так:
MIDDLEWARE_CLASSES = (
'django. middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
# Раскомментируйте следующую строку:
# 'django.middleware.clickjacking.XFrameOptionsMiddleware',
'cms.middleware.page.CurrentPageMiddleware',
'cms.middleware.user.CurrentUserMiddleware',
'cms.middleware.toolbar.ToolbarMiddleware',
'cms.middleware.language.LanguageCookieMiddleware',
)
Как говорится в документации Django CMS, далее необходимо добавить недостающий элемент блока кода в файл. В settings.py. данного раздела не существует. Скопируйте и вставьте данный блок кода в свободное место в файле:
TEMPLATE_CONTEXT_PROCESSORS = (
'django.contrib.auth.context_processors.auth',
'django.core.context_processors.i18n',
'django.core.context_processors.request',
'django. core.context_processors.media',
'django.core.context_processors.static',
'cms.context_processors.media',
'sekizai.context_processors.sekizai',
)
Теперь нужно найти и отредактировать директивы STATIC_ROOT and MEDIA_ROOT следующим образом:
MEDIA_ROOT = os.path.join(PROJECT_PATH, "media")
MEDIA_URL = "/media/"
STATIC_ROOT = os.path.join(PROJECT_PATH, "static")
STATIC_URL = "/static/"
Затем отредактируйте директиву TEMPLATE_DIRS:
TEMPLATE_DIRS = (
os.path.join(PROJECT_PATH, "templates"),
)
Django CMS требует определить хотя бы один шаблон, который нужно затем установить в CMS_TEMPLATES. Внесите следующий блок кода в файл, при необходимости отредактируйте его.
CMS_TEMPLATES = (
('template_1.html', 'Template One'),
)
Также нужно задать язык перевода. Добавьте следующий блок кода:
LANGUAGES = [
('en-us', 'English'),
]
В завершениее задайте процессор базы данных. media/(?P<path>.*)$’, ‘django.views.static.serve’,
{‘document_root’: settings.MEDIA_ROOT, ‘show_indexes’: True}),
url(r», include(‘django.contrib.staticfiles.urls’)),
) + urlpatterns
Имейте в виду, последнее выражение в приведенном выше фрагменте несколько отличается от начальных настроек Django CMS, где строка «urlpatterns = patterns(» заменена строкой «urlpatterns += patterns(» с целью решить проблему переопределения переменной urlpatterns, установленной выше.
Снова нажмите CTRL+X, а затем Y, чтобы сохранить и выйти.
Создайте папку templates:
django_cms$ mkdir templates
Создайте примерный шаблон базы для расширения:
django_cms$ nano templates/base.html
Внесите приведенный ниже фрагмент кода:
{% load cms_tags sekizai_tags %}
<html>
<head>
{% render_block "css" %}
</head>
<body>
{% cms_toolbar %}
{% placeholder base_content %}
{% block base_content %}{% endblock %}
{% render_block "js" %}
</body>
</html>
Сохраните изменения, закройте файл и создайте первый шаблон template_1. html, основнный на базе base.html.
base.html.
django_cms$ nano templates/template_1.html
Внесите сюда следующий фрагмент кода:
{% extends "base.html" %}
{% load cms_tags %}
{% block base_content %}
{% placeholder template_1_content %}
{% endblock %}
Сохраните изменения и закройте файл.
Выполните следующие команды для синхронизации базы данных с настройками:
django_cms$ python manage.py syncdb --all
django_cms$ python manage.py migrate --fake
В завершение нужно проверить, все ли установлено верно, при помощи команды cms check:
django_cms$ python manage.py cms check
Сообщение «Installation okay» означает, что все установлено правильно и теперь можно запустить проект Django CMS на тестовом сервере, прежде чем продолжить его разработку:
django_cms$ python manage.py runserver 0.0.0.0:8000
- Чтобы увидеть экран приветствия Django CMS, перейдите по http:// ip_адрес_сервера:8000/en-us
- Чтобы воспользоваться панелью администратора, перейдите по ссылке http:// ip_адрес_сервера:8000/en-us/admin
Здесь нужо авторизоваться как пользователь, созданный во время синхронизации баз данных. Теперь можно продолжить настройку CMS.
Чтобы получить дополнительные инструкции, перейдите по данной ссылке.
Tags: Debian, Debian 7, Django, Django CMS, Linux, PIL, pillow, pip, PostgreSQL, Python, setuptools, Ubuntu, Ubuntu 13, venv, virtualenv, VPSВаш первый сайт по трясогузкам — Документация по трясогузкам 4.0.2 документация
Примечание
В этом учебном пособии рассказывается о настройке нового проекта «Трясогузка». Если вместо этого вы хотите добавить трясогузку в существующий проект Django, см. раздел Интеграция трясогузки в проект Django.
Установите и запустите трясогузку
Установить зависимости
Трясогузка поддерживает Python 3.7, 3.8, 3.9 и 3.10.
Чтобы проверить, есть ли у вас подходящая версия Python 3:
$ python3 --версия
Если это не возвращает номер версии или возвращает версию ниже 3.7, вам необходимо установить Python 3.
Важно
Перед установкой Трясогузки необходимо установить библиотеки libjpeg и zlib , которые обеспечивают поддержку работы с изображениями JPEG, PNG и GIF (через библиотеку Python Pillow ). Способ сделать это зависит от платформы — см. Pillow’s инструкции по установке для конкретной платформы.
Создать и активировать виртуальную среду
Мы рекомендуем использовать виртуальную среду, которая изолирует установленные зависимости от других проектов.
В этом руководстве используется venv
, который входит в состав Python 3.
В Windows (cmd.exe):
> python3 -m venv мой сайт\env > мой сайт\окружение\скрипты\активировать.bat
В GNU/Linux или MacOS (bash):
$ python3 -m venv мой сайт/окружение $ источник mysite/env/bin/активировать
Другие оболочки см. в документации venv
.
Примечание
Если вы используете контроль версий (например, git), mysite
будет каталогом для вашего проекта.
Каталог env
внутри него должен быть исключен из любого контроля версий.
Установить трясогузку
Используйте pip, входящий в состав Python, для установки Wagtail и его зависимостей:
$ pip установить трясогузку
Создайте свой сайт
Трясогузка предоставляет команду start
, аналогичную django-admin startproject
. Запуск трясогузки start mysite
в вашем проекте создаст новую папку mysite
с несколькими специфичными для трясогузки дополнениями, в том числе
необходимые настройки проекта,
«домашнее» приложение с пустой моделью HomePage
и базовыми шаблонами,
и пример «поискового» приложения.
Поскольку папка mysite
уже создана venv
, запустите старт трясогузки
с дополнительным аргументом для указания каталога назначения:
$ трясогузка start mysite mysite
Примечание
Как правило, в Wagtail каждый тип страницы или тип контента представлен одним приложением. Однако разные приложения могут знать друг о друге и получать доступ к данным друг друга. Все приложения должны быть зарегистрированы в разделе INSTALLED_APPS
файла настроек . Посмотрите на этот файл, чтобы увидеть, как запускается 9Команда 0032 перечислила их там.
Установить зависимости проекта
$ cd мой сайт $ pip install -r требования. txt
Это гарантирует, что у вас есть соответствующие версии Трясогузка, Джанго, и любые другие зависимости для только что созданного проекта.
Создать базу данных
Если вы не обновили настройки проекта, это будет файл базы данных SQLite в каталоге проекта.
$ python manage.py миграция
Эта команда обеспечивает соответствие таблиц в вашей базе данных моделям в вашем проекте. Каждый раз, когда вы изменяете свою модель (например, вы можете добавить поле в модель), вам нужно будет запускать эту команду для обновления базы данных.
Создать пользователя-администратора
$ python manage.py создает суперпользователя
При входе на административный сайт суперпользователь имеет полные права и может просматривать/создавать/управлять базой данных.
Запустить сервер
$ сервер запуска python manage.py
Если все работает, http://127.0.0.1:8000 покажет вам страницу приветствия:
Теперь вы можете получить доступ к административной области по адресу http://127. 0.0.1:8000/admin
Расширение модели домашней страницы
По умолчанию «домашнее» приложение определяет пустую модель HomePage
в models.py
вместе с миграцией, которая создает домашнюю страницу и настраивает трясогузку для ее использования.
Отредактируйте home/models.py
следующим образом, чтобы добавить в модель поле body
:
из моделей импорта django.db со страницы импорта wagtail.models из трясогузки.поля импортировать RichTextField из wagtail.admin.panels импортировать FieldPanel Домашняя страница класса (страница): тело = RichTextField (пусто = Истина) content_panels = Страница.content_panels + [ ПолеПанель('тело'), ]
body
определяется как RichTextField
, специальное поле Трясогузка. Когда пусто = True
,
это означает, что это поле не является обязательным и может быть пустым. Ты
может использовать любое из основных полей Django. content_panels
определяют
возможности и макет интерфейса редактирования. Когда вы добавляете поля в content_panels
, это позволяет редактировать их в интерфейсе Трясогузки. Подробнее о создании моделей страниц.
Запустить python manage.py makemigrations
(это создаст файл миграции), затем python manage.py migrate
(выполняет миграцию и обновляет базу данных вашей моделью
изменения). Вы должны запускать приведенные выше команды каждый раз, когда вносите изменения в
определение модели.
Теперь вы можете редактировать домашнюю страницу в административной области трясогузки (перейдите в «Страницы», «Домашняя страница», затем «Редактировать»), чтобы увидеть новое поле тела. Введите текст в поле body и опубликуйте страницу, выбрав Опубликовать в нижней части редактора страниц, а не Сохранить черновик .
Теперь необходимо обновить шаблон страницы, чтобы отразить внесенные изменения.
к модели. Трясогузка использует обычные шаблоны Django для отображения каждой страницы.
тип. По умолчанию он будет искать имя файла шаблона, образованное из названия приложения и модели,
разделяя заглавные буквы символами подчеркивания (например, домашняя страница в «домашнем» приложении становится home/home_page.html
). Этот файл шаблона может находиться в любом месте, распознаваемом
правила шаблона Django; условно он помещается под 9Папка 0031 templates в приложении.
Изменить home/templates/home/home_page.html
, добавив следующее:
{% расширяет "base.html" %} {% загрузить wagtailcore_tags%} {% block body_class %} шаблон-домашняя страница{% endblock %} {% заблокировать содержимое %} {{ page.body|форматированный текст}} {% конечный блок%}
base.html
относится к родительскому шаблону и всегда должен быть первым тегом шаблона, используемым в шаблоне. Расширение от этого шаблона избавляет вас от необходимости переписывать код и позволяет страницам вашего приложения использовать один и тот же фрейм (используя блочные теги в дочернем шаблоне, вы можете переопределить определенное содержимое в родительском шаблоне).
wagtailcore_tags
также должен быть загружен в верхней части шаблона и содержать дополнительные теги к тем, которые предоставляет Django.
Теги шаблона трясогузки
В дополнение к тегам и фильтрам шаблонов Django,
Трясогузка предоставляет ряд собственных тегов и фильтров для шаблонов.
который можно загрузить, включив {% load wagtailcore_tags %}
вверху
ваш файл шаблона.
В этом руководстве мы используем фильтр RichText для экранирования и печати содержимого.
из RichTextField
:
{% загрузки wagtailcore_tags%} {{ page.body|форматированный текст}}
Производит:
Добро пожаловать на наш новый сайт!
Примечание: Вам необходимо включить {% load wagtailcore_tags %}
в каждый
шаблон, использующий теги Wagtail. Django выдаст TemplateSyntaxError
если теги не загружены.
Базовый блог
Теперь мы готовы создать блог. Для этого запустите python manage.py startapp blog
, чтобы создать новое приложение на вашем сайте Wagtail.
Добавьте новое приложение blog
в INSTALLED_APPS
в mysite/settings/base.py
.
Индекс блога и сообщения
Давайте начнем с простой индексной страницы для нашего блога. В blog/models.py
:
со страницы импорта wagtail.models из трясогузки.поля импортировать RichTextField из wagtail.admin.panels импортировать FieldPanel класс BlogIndexPage (страница): введение = RichTextField (пусто = Истина) content_panels = Страница.content_panels + [ FieldPanel('введение') ]
Запустить python manage.py makemigrations
и python manage.py migrate
.
Поскольку модель называется BlogIndexPage
, имя шаблона по умолчанию
(если мы не переопределим его) будет blog/templates/blog/blog_index_page.html
. Создать этот файл
со следующим содержанием:
{% расширяет "base.html" %} {% загрузить wagtailcore_tags%} {% block body_class %} template-blogindexpage {% endblock %} {% заблокировать содержимое %}{{ page.title }}
{{ page.intro|richtext }}{% для сообщения на странице page.get_children %} {{ post.title }} {{ post.specific.intro }} {{ post.specific.body|форматированный текст}} {% конец для %} {% конечный блок%}
Большая часть этого должна быть вам знакома, но мы объясним get_children
чуть позже.
Обратите внимание на тег pageurl
, который похож на тег Django url
, но
принимает объект страницы трясогузки в качестве аргумента.
В администраторе трясогузки создайте BlogIndexPage
в качестве дочернего элемента домашней страницы,
убедитесь, что у него есть слаг «блог» на вкладке «Продвижение», и опубликуйте его. Теперь у вас должен быть доступ к URL-адресу /blog
на вашем сайте.
(обратите внимание, как слаг на вкладке «Продвижение» определяет URL-адрес страницы).
Теперь нам нужна модель и шаблон для наших сообщений в блоге. В blog/models.py
:
из моделей импорта django.db со страницы импорта wagtail.models из трясогузки.поля импортировать RichTextField из wagtail.admin.panels импортировать FieldPanel из индекса импорта wagtail.search # Сохраните определение BlogIndexPage и добавьте: класс BlogPage (Страница): date = models.DateField("Дата публикации") введение = модели.CharField(max_length=250) тело = RichTextField (пусто = Истина) поля_поиска = поля_поиска_страницы + [ index.SearchField('введение'), index.SearchField('тело'), ] content_panels = Страница.content_panels + [ ПолеПанель('дата'), FieldPanel('введение'), ПолеПанель('тело'), ]
В приведенной выше модели мы импортируем индекс
, так как это делает модель доступной для поиска. Затем вы можете перечислить поля, которые вы хотите сделать доступными для поиска для пользователя.
Запустить python manage.py makemigrations
и python manage.py migrate
.
Создайте шаблон по адресу blog/templates/blog/blog_page.html
:
{% расширяет "base.html" %} {% загрузить wagtailcore_tags%} {% block body_class %} template-blogpage {% endblock %} {% заблокировать содержимое %}{{ page.title }}
{{ page.date }}
{{ page.intro }}{{ page.body|форматированный текст}} {% конечный блок%}
Обратите внимание на использование встроенного метода get_parent()
трясогузки для получения
URL блога, частью которого является этот пост.
Теперь создайте несколько сообщений в блоге как потомков BlogIndexPage
.
Обязательно выберите тип «Страница блога» при создании своих сообщений.
Трясогузка дает вам полный контроль над тем, какие виды контента могут быть созданы под различные типы родительского контента. По умолчанию любой тип страницы может быть дочерним для любого другой тип страницы.
Публикуйте каждый пост в блоге, когда закончите редактирование.
Теперь у вас должны быть зачатки рабочего блога.
Получите доступ к URL-адресу /blog
, и вы должны увидеть что-то вроде этого:
Заголовки должны ссылаться на страницы сообщений, а обратная ссылка — на страницу блога. домашняя страница должна отображаться в нижнем колонтитуле каждой страницы сообщения.
Родители и дети
Большая часть работы, которую вы будете выполнять в Трясогузке, связана с концепцией иерархической
«древовидные» структуры, состоящие из узлов и листьев (см. Теория).
В этом случае BlogIndexPage
— это «узел» и отдельные экземпляры BlogPage
.
это «листья».
Взгляните еще раз на внутренности blog_index_page. html
:
{% для публикации в page.get_children %} {{ post.title }} {{ post.specific.intro }} {{ post.specific.body|форматированный текст}} {% конец для %}
Каждая «страница» в трясогузке может вызывать своего родителя или потомков
со своего положения в иерархии. Но почему мы должны
укажите post.specific.intro
вместо post.intro
?
Это связано с тем, как мы определили нашу модель:
класс Страница блога (Страница):
Метод get_children()
возвращает нам список экземпляров базового класса.
Когда мы хотим ссылаться на свойства экземпляров, которые наследуются от базового класса,
Трясогузка предоставляет специальный метод для , который извлекает фактическую запись
BlogPage
.
В то время как поле «название» присутствует в базе Страница
модель, присутствует только «intro»
на модели BlogPage
, поэтому для доступа к ней нам нужен . specific
.
Чтобы усовершенствовать код шаблона, как этот, мы могли бы использовать Django с тегом
:
{% для публикации в page.get_children %} {% с post=post.specific %} {{ post.title }}{{ post.intro }}
{{ post.body|форматированный текст}} {% конец%} {% конец для %}
Когда вы начнете писать более индивидуальный код трясогузки, вы обнаружите целый набор QuerySet модификаторы, которые помогут вам ориентироваться в иерархии.
# Учитывая объект страницы 'somepage': MyModel.objects.descendant_of(некоторая страница) child_of(страница) / not_child_of(какая-то страница) ancestor_of (какая-то страница) / not_ancestor_of (какая-то страница) parent_of (некоторая страница) / not_parent_of (некоторая страница) sibling_of (какая-то страница) / not_sibling_of (какая-то страница) # ... а также ... некоторая страница.get_children() somepage.get_ancestors() некоторая страница. get_descendants() somepage.get_siblings()
Дополнительные сведения см. в статье: Справочник по Page QuerySet
.Преобладающий контекст
Есть несколько проблем с представлением индекса нашего блога:
Содержание блогов обычно отображается в обратном хронологическом порядке
Мы хотим убедиться, что отображаем только опубликованный контент.
Чтобы добиться этого, нам нужно сделать больше, чем просто получить индекс
дочерние элементы страницы в шаблоне. Вместо этого мы хотим изменить
QuerySet в определении модели. Трясогузка делает это возможным благодаря
непреодолимый метод get_context()
. Измените BlogIndexPage
вот такая модель:
класс BlogIndexPage(Страница): введение = RichTextField (пусто = Истина) def get_context (я, запрос): # Обновить контекст, чтобы включить только опубликованные посты, упорядоченные по обратному хрону контекст = супер(). get_context(запрос) страницы блога = self.get_children().live().order_by('-first_published_at') context['blogpages'] = страницы блога вернуть контекст
Все, что мы здесь сделали, это извлекли исходный контекст, создали собственный QuerySet,
добавьте его в полученный контекст и верните измененный контекст обратно в представление.
Вам также необходимо изменить blog_index_page.html
шаблон немного.
Изменение:
{% для публикации в page.get_children %}
до {% для публикации в blogpages %}
Теперь попробуйте отменить публикацию одного из ваших сообщений — оно должно исчезнуть из индекса блога. страница. Остальные сообщения теперь должны быть отсортированы по последним опубликованным посты первые.
Изображения
Давайте добавим возможность прикреплять галерею изображений к нашим сообщениям в блоге. Хотя можно просто вставлять изображения в body
форматированное текстовое поле, есть несколько преимуществ в настройке изображений нашей галереи в качестве нового выделенного типа объекта в базе данных - таким образом, вы имеете полный контроль над макетом и стилем изображений в шаблоне, вместо того, чтобы разместите их определенным образом в поле форматированного текста. Это также позволяет использовать изображения в другом месте, независимо от текста блога — например, для отображения эскиза на индексной странице блога.
Добавить новый BlogPageGalleryImage Модель
в models.py
:
из моделей импорта django.db # Добавлен новый импорт для ParentalKey, Orderable, InlinePanel из modelcluster.fields импортировать ParentalKey со страницы импорта wagtail.models, можно заказать из трясогузки.поля импортировать RichTextField из wagtail.admin.panels импортировать FieldPanel, InlinePanel из индекса импорта wagtail.search # ... (Сохраните определение BlogIndexPage и обновите BlogPage:) класс BlogPage (Страница): date = models.DateField("Дата публикации") введение = модели.CharField(max_length=250) тело = RichTextField (пусто = Истина) поля_поиска = поля_поиска_страницы + [ index.SearchField('введение'), index.SearchField('тело'), ] content_panels = Страница.content_panels + [ ПолеПанель('дата'), FieldPanel('введение'), ПолеПанель('тело'), InlinePanel('gallery_images', label="Изображения галереи"), ] класс BlogPageGalleryImage (можно заказать): page = ParentalKey(BlogPage, on_delete=models. CASCADE, related_name='gallery_images') изображение = модели.ForeignKey( 'wagtailimages.Image', on_delete=models.CASCADE, related_name='+' ) заголовок = модели.CharField (пробел = Истина, max_length = 250) панели = [ ПолеПанель('изображение'), ПолеПанель('заголовок'), ]
Запустить python manage.py makemigrations
и python manage.py migrate
.
Здесь есть несколько новых понятий, так что давайте рассмотрим их по одному:
Наследование от Orderable
добавляет в модель поле sort_order
для отслеживания порядка изображений в галерее.
ParentalKey
- BlogPage
— это то, что прикрепляет изображения галереи к определенной странице. ParentalKey
работает аналогично ForeignKey
, но также определяет BlogPageGalleryImage
как «дочерний элемент» модели BlogPage
, поэтому он рассматривается как основная часть страницы в таких операциях, как отправка на модерацию и отслеживание истории изменений.
image
— это ForeignKey
для встроенной модели Wagtail
Image
, где хранятся сами изображения. Это отображается в редакторе страниц в виде всплывающего интерфейса для выбора существующего изображения или загрузки нового. Таким образом, мы позволяем изображению существовать в нескольких галереях — по сути, мы создали отношения «многие ко многим» между страницами и изображениями.
Указание on_delete=models.CASCADE
для внешнего ключа означает, что если изображение удаляется из системы, запись галереи также удаляется. (В других ситуациях может быть уместно оставить запись на месте — например, если на странице «наши сотрудники» есть список людей с портретами, и одна из этих фотографий была удалена, мы лучше оставим человека в разместить на странице без фотографии. В этом случае мы установили бы внешний ключ пустым = True, null = True, on_delete = models.SET_NULL
.)
Наконец, добавление InlinePanel
к BlogPage. content_panels
делает изображения галереи доступными в интерфейсе редактирования для BlogPage
.
Настройте шаблон страницы блога, включив в него изображения:
{% расширяет "base.html" %} {% загрузить wagtailcore_tags wagtailimages_tags %} {% block body_class %} template-blogpage {% endblock %} {% заблокировать содержимое %}{{ page.title }}
{{ page.date }}
{{ page.intro }}{{ page.body|форматированный текст}} {% для элемента на странице page.gallery_images.all %} <дел> {% изображение item.image fill-320x240 %}{{ item.caption }}
Здесь мы используем тег {% image %}
(который существует в библиотеке wagtailimages_tags
, импортированной вверху шаблона) для вставки элемента
с параметром fill-320x240
для указать, что изображение должно быть изменено и обрезано, чтобы заполнить прямоугольник размером 320x240. Подробнее об использовании изображений в шаблонах можно прочитать в документации.
Поскольку изображения нашей галереи являются самостоятельными объектами базы данных, теперь мы можем запрашивать и повторно использовать их независимо от тела сообщения в блоге. Давайте определим main_image
метод, который возвращает изображение из первого элемента галереи (или None
, если элементов галереи не существует):
класс BlogPage(Страница): date = models.DateField("Дата публикации") введение = модели.CharField(max_length=250) тело = RichTextField (пусто = Истина) определение main_image (я): Gallery_item = self.gallery_images.first() если элемент_галереи: вернуть Gallery_item.image еще: возврат Нет поля_поиска = поля_поиска_страницы + [ index.SearchField('введение'), index.SearchField('тело'), ] content_panels = Страница.content_panels + [ ПолеПанель('дата'), FieldPanel('введение'), ПолеПанель('тело'), InlinePanel('gallery_images', label="Изображения галереи"), ]
Этот метод теперь доступен в наших шаблонах. Обновите blog_index_page.html
, чтобы добавить основное изображение в качестве эскиза рядом с каждым сообщением:
{% загрузки wagtailcore_tags wagtailimages_tags %} ... {% для публикации на страницах блога %} {% с post=post.specific %} {{ post.title }} {% с post.main_image как main_image %} {% if main_image %}{% image main_image fill-160x100 %}{% endif %} {% конец%}{{ post.intro }}
{{ post.body|форматированный текст}} {% конец%} {% конец для %}
Маркировка сообщений
Допустим, мы хотим, чтобы редакторы «отмечали» свои сообщения, чтобы читатели могли, например,
вместе просматривать весь контент, связанный с велосипедом. Для этого нам нужно вызвать
система тегов в комплекте с трясогузкой, прикрепите ее к BlogPage
панели модели и содержимого, а также визуализировать связанные теги в шаблоне сообщения в блоге.
Конечно, нам также понадобится рабочее представление URL для конкретного тега.
Сначала измените models.py
еще раз:
из моделей импорта django.db # Добавлен новый импорт для ClusterTaggableManager, TaggedItemBase, MultiFieldPanel из modelcluster.fields импортировать ParentalKey из modelcluster.contrib.taggit импортировать ClusterTaggableManager из taggit.models импортировать TaggedItemBase со страницы импорта wagtail.models, можно заказать из трясогузки.поля импортировать RichTextField из wagtail.admin.panels импортировать FieldPanel, InlinePanel, MultiFieldPanel из индекса импорта wagtail.search # ... (сохраните определение BlogIndexPage) класс BlogPageTag (TaggedItemBase): content_object = Родительский ключ ( 'Страница блога', related_name = 'tagged_items', on_delete=модели.КАСКАД ) класс BlogPage (Страница): date = models.DateField("Дата публикации") введение = модели.CharField(max_length=250) тело = RichTextField (пусто = Истина) tags = ClusterTaggableManager (через = BlogPageTag, пусто = True) # . .. (сохраните метод main_image и определение search_fields) content_panels = Страница.content_panels + [ MultiFieldPanel([ ПолеПанель('дата'), ПолеПанель('теги'), ], heading="Информация о блоге"), FieldPanel('введение'), ПолеПанель('тело'), InlinePanel('gallery_images', label="Изображения галереи"), ]
Запустить python manage.py makemigrations
и python manage.py migrate
.
Обратите внимание на новый импорт modelcluster
и taggit
, добавление нового BlogPageTag
и добавление поля tags
на BlogPage
.
Мы также воспользовались возможностью использовать MultiFieldPanel
в content_panels
.
чтобы сгруппировать поля даты и тегов вместе для удобства чтения.
Редактировать один из ваших BlogPage
экземпляров, и теперь вы должны иметь возможность отмечать сообщения:
Чтобы отобразить теги на BlogPage
, добавьте это в blog_page. html
:
{% если page.tags.all.count %} <дел>Теги
{% для тега в page.tags.all %} {% конец для %}