Django is a python based web application framework that is helpful for building a variety of web applications. Django also includes an extensible Django-Admin interface, Default SQLIte3 database, which is also extensible to PostgreSQL, MySQL databases, and a few other components to build efficient web apps.
Install and Setup Django
Create a directory for our blog, install and activate the virtual environment. Then install Django using the below commands
# creating directory for our project mkdir gfgblog && cd gfgblog # installing virtual environment pip install virtualenv python3 -m venv env # activating virtual environment source env/bin/activate # installing django pip install django
As now we have installed Django we will now create a Django project which will set up a basic Django app
django-admin startproject gfgblog cd gfgblog
In our gfgblog Django app, we will have these files
- __init__.py – empty file
- urls.py – for routing our Django project
- settings.py – has all settings for our Django project
- asgi.py, wsgi – helpful while deploying our app
We are in the Django web app directory. Now we will make some migrations for our database which will be SQLite3 which will set up some default tables to run our app in our database. Then we will create a superuser for our app.
# migrating tables python3 manage.py makemigrations python3 manage.py migrate # create and enter the details for superuser python3 manage.py createsuperuser
creating, migrating django-app
Now run the Django development server and open port 8000 in your localhost
# running python development server python3 manage.py runserver
default django page
Now stop the server and go into gfgblog directory in our gfgblog Django app directory and open urls.py file using your code editor. You can look at the tree of our gfgblog directory from the below picture
django app directory tree structure
Create a Blog App in Django –
Now we will create our actual blog app and database for it. Go to the gfgblog project directory. You can see our SQLite3 database, gfgblog Django app. now in this directory create a new app named blog. The below command will create a new app for us.
# creating an app named blog python3 manage.py startapp blog
The new app directory has 5 default files
- __init__.py – an empty file
- admin.py – for managing admin interface
- apps.py – for managing app configuration
- models.py – for managing database models of the app
- tests.py – for testing the app
- views.py – for managing behavior and logic of the app
As we have created the app now we have to tell the django app that there is a new app in our project. For that go to settings.py in gfgblog directory and open settings.py file. Go to installed apps section of the settings file and add the name of the app we created; in our case its blog
Python3
INSTALLED_APPS
=
[
'django.contrib.admin'
,
'django.contrib.auth'
,
'django.contrib.contenttypes'
,
'django.contrib.sessions'
,
'django.contrib.messages'
,
'django.contrib.staticfiles'
,
'blog'
,
]
Creating Models for Blog CMS –
Now we will create Django models for the app. You can learn more about Django Models from this tutorial – Django Models
To create Django Models open in your editor and add the below code
Python3
from
django.db
import
models
from
django.contrib.auth.models
import
User
STATUS
=
(
(
0
,
"Draft"
),
(
1
,
"Publish"
),
(
2
,
"Delete"
)
)
class
posts(models.Model):
title
=
models.CharField(max_length
=
200
, unique
=
True
)
slug
=
models.SlugField(max_length
=
200
, unique
=
True
)
author
=
models.ForeignKey(User, on_delete
=
models.CASCADE)
updated_on
=
models.DateTimeField(auto_now
=
True
)
created_on
=
models.DateTimeField()
content
=
models.TextField()
metades
=
models.CharField(max_length
=
300
, default
=
"new post"
)
status
=
models.IntegerField(choices
=
STATUS, default
=
0
)
class
Meta:
ordering
=
[
'-created_on'
]
def
__str__(
self
):
return
self
.title
Save the file and make migration so that the fields will be created in our database using the below commands
# creates migrations for the blog app python3 manage.py makemigrations blog # migrates the blog app python3 manage.py migrate blog
So far we are good with creating models and setting our database for storing our posts, but we should present them to users. Luckily Django comes with its own templating language with which we can build dynamic HTML pages to serve our users. We can also add styling using CSS and JavaScript. You can learn more about Django templates in this tutorial – Django Templates
Creating Templates for Blog CMS –
To create templates first create a template directory in the blog app (You can also create it outside the blog app but in this way, it would be easy to manage). To manage templates in an easy way for each app in the project we have to do some changes in the settings. Go to the project settings file and replace template settings with the code below
Python3
TEMPLATES_DIR
=
os.path.join(BASE_DIR,
'templates'
)
TEMPLATES
=
[
{
'BACKEND'
:
'django.template.backends.django.DjangoTemplates'
,
'DIRS'
: [TEMPLATES_DIR],
'APP_DIRS'
:
True
,
'OPTIONS'
: {
'context_processors'
: [
'django.template.context_processors.debug'
,
'django.template.context_processors.request'
,
'django.contrib.auth.context_processors.auth'
,
'django.contrib.messages.context_processors.messages'
,
],
},
},
]
Now we will create templates for our blog. As Django can build pages dynamically we will build our templates according to it. Now we will create a base template that serves as a base for all the pages in our blog.
HTML
<!DOCTYPE html>
<
html
lang
=
"en"
>
<
head
>
{% block metatags %}{% endblock %}
<
meta
charset
=
"UTF-8"
>
<
meta
name
=
"viewport"
content
=
"width=device-width, initial-scale=1"
/>
<
style
>
html, body {
height: 100%;
}
body {
font-family: sans-serif;
font-size: 18px;
background-color: #fdfdfd;
display: flex;
flex-direction: column;
}
#nofooter {
flex: 1 0 auto;
}
darkbtn {
color: #66ff99; }
.dark-mode {
background-color: black;
color: white;
}
#foot {
flex-shrink: 0;
font-family: sans-serif;
background-color: #BDFFD3; color: #00308F;
bottom: 0px;
width: 100%;
}
.card-group,.card,.card-footer,.card-body {
border: none;
}
</
style
>
</
head
>
<
body
><
div
id
=
"bodydiv"
><
div
id
=
"nofooter"
>
<
nav
class
=
"navbar navbar-expand-lg navbar-light bg-light shadow"
id
=
"topnav"
>
<
div
class
=
"container-fluid"
>
<
a
class
=
"navbar-brand"
href
=
"{% url 'home' %}"
>
<
img
src
=
"/static/icons/logo.png"
width
=
"30"
height
=
"30"
class
=
"d-inline-block rounded align-top"
alt
=
"logo"
loading
=
"lazy"
>
GeeksForGeeks</
a
>
<
div
class
=
"nav justify-content-end"
>
<
div
class
=
"d-inline"
><
a
class
=
"nav-link text-black font-weight-bold"
href
=
"{% url 'posts'%}"
>Posts</
a
></
div
>
</
div
></
div
>
</
nav
>
{% block content %}
{% endblock content %}
</
div
>
<
footer
class
=
"footer"
id
=
"foot"
>
<
div
class
=
"container"
><
h5
style
=
"text-align:center;"
>Enjoy the Blog and Stay Updated</
h5
>
<
nav
class
=
"navbar navbar-expand-lg"
>
<
div
class
=
"container-fluid"
>
<
p
>Copyright © GeeksForGeeks</
p
>
<
div
class
=
"nav justify-content-center"
id
=
"navfooter"
>
<
span
><
a
class
=
"nav-link text-black font-weight-bold"
href
=
"{% url 'about' %}"
>About</
a
></
span
>
<
span
><
a
class
=
"nav-link text-black font-weight-bold"
href
=
"{% url 'privacy' %}"
>Privacy</
a
></
span
>
<
span
><
a
class
=
"nav-link text-black font-weight-bold"
href
=
"{% url 'tos' %}"
>Terms</
a
></
span
>
<
span
><
a
class
=
"nav-link text-black font-weight-bold"
href
=
"#"
>Feed</
a
></
span
>
</
div
>
</
div
>
</
nav
>
</
div
>
</
footer
></
div
>
</
body
>
</
html
>
This will be our base template, You can block like {% block name %}{% endblock %} which will be replaced with the content assigned to them, Now we will create a home template that would be the homepage for our blog which will have the latest posts.
HTML
{% extends "base.html" %}
{% block metatags %}
<
title
>Home | GeeksForGeeks</
title
>
<
meta
name
=
"description"
content
=
"A destination for Learning"
>
<
meta
property
=
"og:title"
content
=
"GeeksForGeeks"
>
<
meta
property
=
"og:site_name"
content
=
"GeeksForGeeks"
>
<
meta
property
=
"og:description"
content
=
"A destination for Learning"
>
<
meta
property
=
"og:type"
content
=
"website"
>
{% endblock %}
{% block content %}
<
style
type
=
"text/css"
>
body {
font-family: 'Raleway', sans-serif;}
.head_text {
color: white;
}
.h1, h4 {
font-family: 'Raleway', sans-serif;
}
#mainhome {
text-align: center; }
</
style
>
<
header
class
=
"jumbotron"
style
=
"background-color: #BDFFD3; color: #00308F;"
>
<
div
class
=
"container"
>
<
p
class
=
"h1"
> Welcome to <
strong
>GeeksForGeeks</
strong
></
p
>
</
div
>
</
header
>
<
div
class
=
"container"
>
<
div
class
=
"row"
>
<
div
class
=
"col-md-8 mt-3 left"
>
{% for post in posts_list %}
<
div
class
=
"shadow-none card mb-4"
id
=
"newsfeed"
>
<
div
class
=
"card-body"
>
<
h3
class
=
"card-title"
>{{ post.title }}</
h3
>
<
p
class
=
"card-text text-muted h6"
><
span
>{{ post.author.first_name }} {{ post.author.last_name }}</
span
> | {{ post.created_on}} </
p
>
<
p
class
=
"card-text"
>{{post.metades }}</
p
>
<
span
><
a
href
=
"{% url 'post_detail' post.slug %}"
class
=
"btn btn-outline-primary"
>Read More →</
a
></
span
><
span
><
a
href
=
"{% url 'post_detail_amp' post.slug %}"
class
=
"btn btn-outline-success"
>AMP page</
a
></
span
>
</
div
>
</
div
>
{% endfor %}
</
div
>
</
div
>
</
div
>
{% if is_paginated %}
<
nav
aria-label
=
"Page navigation container"
></
nav
>
<
ul
class
=
"pagination justify-content-center"
>
{% if page_obj.has_previous %}
<
li
><
a
href
=
"?page={{ page_obj.previous_page_number }}"
class
=
"btn btn-outline-info"
style
=
"font-family: sans-serif;"
>« PREV </
a
></
li
>
{% endif %}
{% if page_obj.has_next %}
<
li
><
a
href
=
"?page={{ page_obj.next_page_number }}"
class
=
"btn btn-outline-info"
style
=
"font-family: sans-serif;"
> NEXT »</
a
></
li
>
{% endif %}
</
ul
>
{% endif %}
{%endblock%}
We have created our homepage with pagination. Posts will be displayed using the for a loop. Now we will add our last template to display posts individually.
HTML
{% extends 'base.html' %}
{% block metatags %}
<
title
>{{ object.title }}</
title
>
<
meta
name
=
"description"
content
=
"{{ object.metades}}"
/>
<
meta
property
=
"og:title"
content
=
"{{ object.title }}"
>
<
meta
property
=
"og:site_name"
content
=
"GeeksForGeeks"
>
<
meta
property
=
"og:url"
content
=
"{% url 'post_detail' object.slug %}"
>
<
meta
property
=
"og:description"
content
=
"{{ object.metades }}"
>
<
meta
property
=
"og:type"
content
=
"article"
>
{% endblock %}
{% block content %}
<
style
type
=
"text/css"
>
#container img {
border-radius: 29px;
width: 100%;
height: 360px;
opacity: 0.7;
align-content: center;
}
#container img {
opacity: 1.0; }
a {text-align: center; text-decoration: none;}
</
style
>
<
script
type
=
"application/ld+json"
>
{
"@type": "Post",
"headline": "{{ object.title }}",
"description": "{{ object.metades }}",
"mainEntityOfPage": {
"@type": "WebPage",
"@id": "{% url 'post_detail' object.slug %}"
},
"author": {
"@type": "Person",
"name": "{{ post.author.first_name }} {{ post.author.last_name "
},
"publisher": {
"@type": "Organization",
"name": "GeeksForGeeks",
},
"datePublished": "{{ news.created_on }}",
"dateModified": "{{ news.created_on }}",
"mentions": "{{ object.source }}"
}
</
script
>
<
div
class
=
"container"
>
<
div
class
=
"row"
>
<
div
class
=
"col-md-6 left"
>
<
h1
class
=
"card-title"
>{% block title %} {{ object.title }} {% endblock title %}</
h1
>
<
p
class
=
" text-muted"
>{{ object.author.first_name }} {{ object.author.last_name }} |{{ object.created_on }}</
p
>
<
p
class
=
"card-text"
>{{ object.content | safe }}</
p
>
</
div
>
</
div
>
</
div
>
{% endblock content %}
Both homepage and post page will be built by extending the base template and replacing blocks with the data stored in our database. Both also have a Facebook open graphs for better sharing and social network and the post page has a structured schema for better SEO additionally. We will build AMP template incoming tutorial.
Creating Views for Blog CMS –
Now open views.py file in the blog directory. This file has the logic to run the blog app. We will be using class-based views for the blog app. Class Based Generic Views are an advanced set of Built-in views which are used for the implementation of selective view strategies such as Create, Retrieve, Update, Delete.
Python3
from
django.shortcuts
import
render
from
.models
import
posts
from
django.views
import
generic
from
django.views.decorators.http
import
require_GET
from
django.http
import
HttpResponse
class
postslist(generic.ListView):
queryset
=
posts.objects.
filter
(status
=
1
).order_by(
'-created_on'
)
template_name
=
'home.html'
paginate_by
=
4
class
postdetail(generic.DetailView):
model
=
posts
template_name
=
"post.html"
Creating Routes for Blog CMS –
For Routing our blog app just go into the blog app directory and create a file urls.py to route our app. see the code below
Python3
from
.
import
views
from
django.urls
import
path, include
from
.views
import
*
from
.feeds
import
blogFeed
urlpatterns
=
[
path('
', views.postslist.as_view(), name='
posts'),
path(
'<slug:slug>/'
, views.postdetail.as_view(), name
=
'post_detail'
),
]
Go to urls.py file in gfgblog directory and route our blog app. See the code below for reference
Python3
from
django.contrib
import
admin
from
django.urls
import
path, include, re_path
from
django.conf
import
settings
from
django.conf.urls.static
import
static
urlpatterns
=
[
path(
'admin/'
, admin.site.urls),
path('
', include('
blog.urls')),
]
Done, Let’s Now run the server using
Python manage.py runserver
Blog CMS Screenshots –
HomePage –
homepage with latest’s posts
A sample post page –
sample post page
Posts management with Django Admin
posts management
User management via Django Admin
User management
В мире 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 |
На VPS у меня уже был установлен Python 3.6.7 но если по каким либо причинам его у вас нет, то устанавливаем его таким образом:
Далее, устанавливаем необходимые библиотеки:
$ 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 |
Все манипуляции и установки через pip выполняем исключительно в виртуальном окружении, чтобы не засорять ненужными библиотеками главное окружение. В папке /home
создаем новую папку /home/web
Находясь в папке /home/web
мы создадим виртуальное окружение для нашего приложения:
python3 —m venv venv source venv/bin/activate |
Вводная строка терминала изменилась на что-то подобное:
(venv) user@kvmde58—14996:/home/web# |
Установка Wagtail и его зависимостей:
Запустите сайт:
$ wagtail start mysite $ cd mysite |
Wagtail предоставляет команду start
, аналогичную django-admin.py startproject
. Запуск wagtail start mysite
в вашем проекте создаст новую папку mysite
со специальными дополнениями Wagtail, включая необходимые настройки проекта, приложение “home” с пустой домашней страницей, основными шаблонами и примером приложения “search”.
Установка проектных зависимостей
$ pip install —r requirements.txt |
Здесь указывается, что у вас подходящая версия Django для созданного вами проекта.
Создание базы данных
$ python manage.py migrate |
Если вы не обновили настройки проекта, то у вас будет файл базы данных SQLite в папке проекта.
Создание пользователя admin:
$ python manage.py createsuperuser |
Запуск сервера:
$ 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
в модель:
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 %} |
Wagtail предоставляет ряд тегов шаблонов и фильтров, которые можно загрузить внесением {% load wagtailcore_tags %}
в начале вашего файла шаблона.
В данном руководстве мы используем фильтр richtext
для экранирования и вывода содержимого RichTextField
:
{% load wagtailcore_tags %} {{ page.body|richtext }} |
Генерирует:
<div class=«rich-text»> <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
:
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
. Создайте этот файл со следующим содержимым:
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 %} <h1>{{ page.title }}</h1> <div class=«intro»>{{ page.intro|richtext }}</div> {% for post in page.get_children %} <h2><a href=«{% pageurl post %}»>{{ post.title }}</a></h2> {{ 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
:
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
:
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 %} <h1>{{ page.title }}</h1> <p class=«meta»>{{ page.date }}</p> <div class=«intro»>{{ 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 %} <h2><a href=«{% pageurl post %}»>{{ post.title }}</a></h2> {{ post.specific.intro }} {{ post.specific.body|richtext }} {% endfor %} |
Каждая “страница” в Wagtail может вызывать дочернюю или родительскую страницу со своей позиции в иерархии. Но почему мы должны определять post.specific.intro
вместо post.intro
? Это напрямую связано с тем, как мы определили нашу модель:
Метод get_children()
выдает нам список экземпляров основного класса Page
. Когда нам нужно сослаться на свойства экземпляров, которые наследуются от базового класса, Wagtail предоставляет особый метод, который возвращает фактическую запись BlogPage
. В то время как поле “title” присутствует в базовой модели Page
, “intro” присутствует только в модели BlogPage
, так что нам нужен .specific
для получения доступа.
Чтобы сжать код шаблона, мы можем использовать тег Django под названием with
:
{% for post in page.get_children %} {% with post=post.specific %} <h2><a href=«{% pageurl post %}»>{{ post.title }}</a></h2> <p>{{ post.intro }}</p> {{ post.body|richtext }} {% endwith %} {% endfor %} |
Когда вы начинаете вписывать больше персонализированного когда в Wagtail, вы увидите целый набор модификаторов QuerySet
, которые помогут вам ориентироваться по иерархии.
# Задан объект страницы ‘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
проходит следующим образом:
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
:
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
.
Настройте шаблон страницы блога, чтобы включить изображения:
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 %} <h1>{{ page.title }}</h1> <p class=«meta»>{{ page.date }}</p> <div class=«intro»>{{ page.intro }}</div> {{ page.body|richtext }} {% for item in page.gallery_images.all %} <div style=«float: left; margin: 10px»> {% 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
, если в галерее нет элементов):
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 %} <h2><a href=«{% pageurl post %}»>{{ post.title }}</a></h2> {% 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
еще раз:
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 class=«tags»> <h3>Tags</h3> {% 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
:
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
:
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 %} <h4>Showing pages tagged «{{ request.GET.tag }}»</h4> {% 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
следующее:
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
для отношений “один ко многим”.
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
для отображения категорий:
<h1>{{ page.title }}</h1> <p class=«meta»>{{ page.date }}</p> {% with categories=page.categories.all %} {% if categories %} <h3>Posted in:</h3> <ul> {% for category in categories %} <li style=«display: inline»> {% image category.icon fill-32×32 style=»vertical-align: middle» %} {{ category.name }} </li> {% endfor %} </ul> {% endif %} {% endwith %} |
Являюсь администратором нескольких порталов по обучению языков программирования Python, Golang и Kotlin. В составе небольшой команды единомышленников, мы занимаемся популяризацией языков программирования на русскоязычную аудиторию. Большая часть статей была адаптирована нами на русский язык и распространяется бесплатно.
E-mail: vasile.buldumac@ati.utm.md
Образование
Universitatea Tehnică a Moldovei (utm.md)
- 2014 — 2018 Технический Университет Молдовы, ИТ-Инженер. Тема дипломной работы «Автоматизация покупки и продажи криптовалюты используя технический анализ»
- 2018 — 2020 Технический Университет Молдовы, Магистр, Магистерская диссертация «Идентификация человека в киберпространстве по фотографии лица»
django CMS quickstart
A Dockerised django CMS project, ready to deploy on Divio or another Docker-based cloud platform, and run locally in Docker on your own machine.
This version uses Python 3.9 running and the most up-to-date versions of Django 3.2 and django CMS 3.11.
This project is endorsed by the django CMS Association. That means that it is officially accepted by the dCA as being in line with our roadmap vision and development/plugin policy. Join us on Slack for more information or questions.
Installation
You need to have docker installed on your system to run this project.
- Install Docker here.
- If you have not used docker in the past, please read this introduction on docker here.
Try it
git clone git@github.com:django-cms/django-cms-quickstart.git
cd django-cms-quickstart
docker compose build web
docker compose up -d database_default
docker compose run web python manage.py migrate
docker compose run web python manage.py createsuperuser
docker compose up -d
Then open http://django-cms-quickstart.127.0.0.1.nip.io:8000 (or just http://127.0.0.1:8000) in your browser.
Note: Since Compose V2, docker-compose
is now included inside docker. For more information, checkout the Compose V2 Documentation.
Customising the project
This project is ready-to-go without making any changes at all, but also gives you some options.
As-is, it will include a number of useful django CMS plugins and Bootstrap 5 for the frontend. You don’t have to use
these; they’re optional. If you don’t want to use them, read through the settings.py
and requirements.txt
files to
see sections that can be removed — in each case, the section is noted with a comment containing the word ‘optional’.
Options are also available for using Postgres/MySQL, uWSGI/Gunicorn/Guvicorn, etc.
Updating requirements
The project uses a 2 step approach, freezing all dependencies with pip-tools. Read more about how to handle it here: https://blog.typodrive.com/2020/02/04/always-freeze-requirements-with-pip-compile-to-avoid-unpleasant-surprises/
Features
Static Files with Whitenoise
This quickstart demo has a cloud-ready static files setup via django-whitenoise.
In the containerized cloud the application is not served by a web server like nginx but directly through uwsgi. django-whitenoise is the glue that’s needed to serve static files in your application directly through uwsgi.
See the django-whitenoise settings in settings.py and the quickstart/templates/whitenoise-static-files-demo.html
demo page template that serves a static file.
Contribution
Here is the official django CMS repository: https://github.com/django-cms/django-cms-quickstart/.
Deployment
Note that this is just a demo project to get you started. If you want a full production ready site with all the bells and whistles we recommend you have a look at https://github.com/django-cms/djangocms-template instead.
Env variables
- to deploy this project in testing mode (recommended) set the environment variable
DEBUG
toTrue
in your hosting environment. - For production environment (if
DEBUG
is false) django requires you to whitelist the domain. Set the env varDOMAIN
to the host, i.e.www.domain.com
or*.domain.com
. - If you want the media hosted on S3 set the
DEFAULT_FILE_STORAGE
variable accordingly.
Deployment Commands
Configure your hosting environment to run the following commands on every deployment:
./manage.py migrate
Divio Deployment
divio.com is a cloud hosting platform optimized for django web applications. It’s the quickest way to deploy this project. Here is a video tutorial and a description of the deployment steps that are mostly applicable for this quickstart project.
Я уже очень давно слежу за развитием проекта DjangoCMS и честно скажу что он не всегда мне нравился. Но постепенно команда проекта исправила многие косяки и сделала эту CMS по-настоящему удобной, гибкой и функциональной. После этого я стал практически фанатом DjangoCMS и использую его в некоторых своих проектах (например этот блог). Сегодня я подробно расскажу что такое DjangoCMS, в каких случаях его удобно применять и покажу как с помощью него создать простой сайт. Надеюсь вы тоже полюбите этот инструмент
На данный момент основная проблема DjangoCMS — отсутствие нормальной документации. Первое что я нашёл на официальном сайте проекта, это инструкция как создать сайт с помощью DjangoCMS и развернуть его на облачной платформе Divio Cloud. Из этой инструкции конечно можно выцепить какое-то количество полезной информации, но я как разработчик, жду совсем другого, тем более что я не планирую использовать Divio Cloud, а хочу развернуть приложение на DjangoCMS где-нибудь у себя. Самое интересное, что существует официальная документация для разработчиков по DjangoCMS, но данную ссылку я нашёл в файле README.md из репозитория проекта на GitHub, в то время как на сайте её почему-то нет.
Есть одна вещь, за которую стоит отдельно похвалить команду Divio — грамотная поддержка в online-чате на сайте проекта DjangoCMS, куда можно задавать вопросы любой сложности! Многие вещи о которых не сказано в документации я узнал лично от ребят из Divio во время переписки с ними. Там есть и наши соотечественники которые отвечали по русски, за что им отдельный респект
Хватит лирики, приступаем к созданию собственного сайта на DjangoCMS!
Установка DjangoCMS
В начале необходимо создать и активировать виртуальное окружение для нашего проекта. Для этого выполните команду:
virtualenv env source env/bin/activate
Если вы хотите использовать в своём проекте Python 3, то первую команду нужно заменить на:
virtualenv --python=/usr/bin/python3.5 env
Только не забудьте проверить правильность пути к интерпретатору Python 3, так как у вас он может отличаться.
После того как виртуальное окружение создано и активировано нам нужно поставить установщик DjangoCMS — пакет djangocms-installer:
pip install djangocms-installer
djangocms-installer ставит все необходимые пакеты для работы DjangoCMS. После этого ваше виртуальное окружение начинает поддерживать команду djangocms, которой мы скоро воспользуемся.
Создаём директорию для нашего проекта и переходим в неё:
mkdir django_cms_project cd django_cms_project
И наконец создаём наше приложение на DjangoCMS с помощью команды:
djangocms -f -p . django_cms_example
- Флаг -f говорит о том что дополнительно будет установлен инструмент Django Filer, который отвечает за работу с файловой системой. Мне данный инструмент нравится и я рекомендую его использовать.
- Флаг -p с точкой (-p .) задаёт текущую директорию как родительскую для проекта.
- djangocms_blog — название нашего приложения. Вы можете указать своё собственное.
В старых версиях установщик DjangoCMS задавал вопросы по конфигурированию приложения во время установки. В новой версии приложение конфигурируется автоматически, а так же создаётся суперпользователь с логином admin и паролем admin. После установки вы можете запустить приложение командой:
python manage.py runserver
После выполнения команды откройте в браузере ссылку http://127.0.0.1:8000/. Если всё сделано правильно, вы должны увидеть форму входа в панель администрирования Django:
Для входа просто введите логин admin и пароль admin. На этом процесс установки DjangoCMS можно считать завершённым.
Настройка
Откройте в своей любимой IDE (лично я предпочитаю PyCharm) только что созданный проект. В моём случае файлы проекта располагаются в директории djangocms_blog_project. Структура созданного проекта должна быть примерно такой:
Как Вы видите, это обычный Django-проект, только с прикрученным DjangoCMS в настройках (файл settings.py). В целом наш проект и так прекрасно запускается и работает, но Вы должны помнить что по умолчанию будет использоваться база данных SQLite, которая хорошо подходит для отладки, но совсем не подходит для работы в реальных условиях. Кроме этого DjangoCMS очень много информации хранит именно в базе: контент, меню, список страниц, настройки каждой страницы, посты и т.д. То есть при переходе на другую базу данных придётся делать миграцию и я рекомендую сразу использовать ту базу данных, дамп которой можно потом перенести на продакшен. Но это просто рекомендация, можно успешно отладить приложение, используя SQLite и фейковую информацию на страницах, а затем на новой базе собрать чистовой вариант уже на отлаженных шаблонах. Или же, как правильно заметил Dmytro Kyrychuk в комментариях, использовать команды manage.py dumpdata и loaddata (эти команды подробно описаны тут: https://docs.djangoproject.com/en/1.10/ref/django-admin/) для того, чтобы сдампить одну базу в JSON и восстановить дамп уже на другой базе.
Так как мой проект демонстрационный, я оставляю настройки базы данных без изменений:
DATABASES = {
'default': {
'CONN_MAX_AGE': 0,
'ENGINE': 'django.db.backends.sqlite3',
'HOST': 'localhost',
'NAME': 'project.db',
'PASSWORD': '',
'PORT': '',
'USER': ''
}
}
Других настроек на данном этапе не требуется.
Создание шаблонов
Прежде чем создавать страницы нашего сайта-блога нам нужны шаблоны на основе которых будут создаваться эти самые страницы. Наверняка вы захотите использовать собственный неповторимый дизайн чтоб было лучше чем у соседа, поэтому нам нужно в начале погрузиться в вёрстку. Шаблоны DjangoCMS это обычные файлы в формате .html с некоторым количеством шаблонных тегов Django и DjangoCMS. При создании нового проекта автоматически генерируется 4 шаблона в папке templates (в моём случае djangocms_blog_project/djangocms_blog/templates):
- base.html — базовый шаблон, от которого наследуются все остальные шаблоны
- fullwidth.html — шаблон полноразмерной страницы с контентом на всю ширину экрана
- sidebar_left.html — страница с левым сайдбаром
- sidebar_right.html — страница с правым сайдбаром
Кроме того, список шаблонов обязательно должен быть задан в settings.py в переменной CMS_TEMPLATES для того чтобы можно было выбрать нужный шаблон в интерфейсе при создании новой страницы сайта. По умолчанию у нас сконфигурировано 3 шаблона (все кроме base.html):
CMS_TEMPLATES = (
('fullwidth.html', 'Fullwidth'),
('sidebar_left.html', 'Sidebar Left'),
('sidebar_right.html', 'Sidebar Right')
)
Тут всё просто: нужно задать реальное название файла шаблона и произвольное название, которое будет отображаться в интерфейсе. Если вы создаёте новый шаблон, то в обязательном порядке вносите его в CMS_TEMPLATES, иначе вы не сможете им воспользоваться.
Давайте создадим первую страницу с использованием тех шаблонов, что у нас уже есть. После того как вы вошли в панель администрирования под логином admin (см. выше), должны увидеть вот такую страницу:
Выберите пункт Новая страница (Создать страницу рядом с текущей) и нажмите Далее. Откроется вот такое окно:
Заполните заголовок (Главная), путь к странице (index) и содержимое (Hello world!), а затем нажмите кнопку Создать. Всё! Страница готова:
Теперь в верхнем меню выберите Страница > Шаблоны:
В выпадающем меню жирным шрифтом должен быть выделен текущий шаблон страницы, но так как мы его явно не задавали, то по умолчанию стоит значение Наследовать шаблон от ближайшего предка. В нашем случае ближайшим предком является шаблон fullwidth.html. Выберите его и вы увидите что ничего не изменилось в дизайне страницы.
Теперь предлагаю подробно разобрать код шаблона fullwidth.html, но так как он наследуется от base.html, то начнём с базового шаблона:
{% load cms_tags menu_tags sekizai_tags %}
<!doctype html>
<html>
<head>
<title>{% block title %}This is my new project home page{% endblock title %}</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<style type="text/css">
.nav {
padding-left: 0;
}
.nav li {
display: inline;
list-style-type: none;
padding-right: 20px;
}
.container {
width: 940px;
margin: 0 auto
}
.content {
float: left;
width: 80%;
}
.sidebar {
float: left;
width: 20%;
}
</style>
{% render_block "css" %}
</head>
<body>
{% cms_toolbar %}
<div class="container">
<ul class="nav">
{% show_menu 0 100 100 100 %}
</ul>
{% block content %}{% endblock content %}
</div>
{% render_block "js" %}
</body>
</html>
Что тут происходит? Да ничего особенного. В первой строке мы загружаем шаблонные теги:
- cms_tags — теги DjangoCMS (http://docs.django-cms.org/en/release-3.4.x/reference/templatetags.html)
- menu_tags — теги для управления меню (http://docs.django-cms.org/en/release-3.4.x/reference/navigation.html)
- sekizai_tags — теги django-sekizai (https://django-sekizai.readthedocs.io/en/latest/)
Так же у нас в шаблоне 2 блока, которые можно переопределить в дочерних шаблонах: {% block title %} и {% block content %}.
Кроме этого у нас есть:
- тег ответственный за отображение тулбара DjangoCMS — {% cms_toolbar %}
- теги django-sekizai, которые отвечают за размещение блоков со ссылками на css и js в определённых частях страницы — {% render_block «css» %} и {% render_block «js» %}
- тег, ответственный за отображение меню — {% show_menu 0 100 100 100 %}
Всё остальное вы и так знаете. Переходим к шаблону fullwidth.html:
{% extends "base.html" %}
{% load cms_tags %}
{% block title %}{% page_attribute "page_title" %}{% endblock title %}
{% block content %}
{% placeholder "content" %}
{% endblock content %}
По факту перед нами стандартный шаблон Django отнаследованный от base.html. Из интересного тут 2 тега DjangoCMS:
- тег {% page_attribute «page_title» %} — отображает атрибут страницы page_title в блоке title. Данный атрибут задаётся при создании страницы и является её заголовком. Все атрибуты можно просмотреть в документации.
- тег {% placeholder «content» %} — это очень важный тег, который задаёт изменяемую часть страницы в блоке content. Количество плейсхолдеров на странице может быть неограниченным, главное чтоб название каждого плейсхолдера было уникальным. В панели администратора DjangoCMS можно менять содержимое любого плейсхолдера на своё усмотрение, например туда можно добавить текст, ссылку, картинку и т.д., а можно всё сразу и в любых комбинациях
Вот вы уже имеете некоторое представление о шаблонах DjangoCMS. Приведённые выше шаблоны я полностью изменю и доработаю, так как хочу чтобы сайт имел красивый дизайн. Это всё реализуется очень просто, но прежде чем менять шаблоны, давайте заглянем в панель администрирования DjangoCMS, для того чтобы лучше разобраться с плейсхолдерами. Нажмите кнопку Структура на верхней панели и вы увидите вот такую страницу:
Содержимое — это наш плейсхолдер content из шаблона fullwidth.html. Он содержит один единственный плагин, который отображает строчку Hello world!. Вы можете в любой момент отредактировать содержимое плагина, а так же добавлять другие плагины в плейсхолдер и даже создавать свои собственные плагины (об этом я расскажу в другой статье).
Для наглядности добавим ещё один плагин в плейсхолдер content. Для этого нажмите кнопку + и в появившемся окне выберите из списка плагин Google Map:
В открывшемся окне задайте название карты (поле MAP TITLE), а так же широту и долготу своего города (поля LATITUDE и LONGITUDE). В качестве названия у меня просто слово Карта, широта 55.751244, долгота 37.618423:
Нажмите кнопку Сохранить и вы увидите в списке плагинов плейсхолдера contentновый плагин:
В верхней панели появилась синяя кнопка Опубликовать изменения страницы. Нажмите её и вы увидите обновлённую главную страницу с картой:
Вот так легко и просто можно управлять содержимым страницы в DjangoCMS.
Теперь я покажу как кардинально изменить дизайн сайта. Больше всего мне нравится в DjangoCMS то что можно взять практически любой html-шаблон и с минимальными изменениями встроить его в свой проект. В качестве примера я нашёл в интернете бесплатный адаптивный шаблон, свёрстанный с помощью CSS-фреймворка Bootstrap.
Для начала изменим содержимое базового шаблона base.html:
{% load cms_tags menu_tags sekizai_tags %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="">
<title>{% block title %}Сайт Алексея Кутепова{% endblock title %}</title>
<!-- Bootstrap Core CSS -->
<link href="{{ STATIC_URL }}css/bootstrap.min.css" rel="stylesheet">
<!-- Theme CSS -->
<link href="{{ STATIC_URL }}css/clean-blog.min.css" rel="stylesheet">
<!-- Custom Fonts -->
<link href="{{ STATIC_URL }}css/font-awesome.min.css" rel="stylesheet" type="text/css">
<link href='https://fonts.googleapis.com/css?family=Lora:400,700,400italic,700italic' rel='stylesheet' type='text/css'>
<link href='https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800' rel='stylesheet' type='text/css'>
{% render_block "css" %}
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->
</head>
<body>
{% cms_toolbar %}
<!-- Navigation -->
<nav class="navbar navbar-default navbar-custom navbar-fixed-top">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header page-scroll">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
<span class="sr-only">Меню</span>
Меню <i class="fa fa-bars"></i>
</button>
<a class="navbar-brand" href="/">Алексей Кутепов</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav navbar-right">
{% show_menu 0 100 100 100 %}
</ul>
</div>
<!-- /.navbar-collapse -->
</div>
<!-- /.container -->
</nav>
<!-- Page Header -->
<!-- Set your background image for this header on the line below. -->
{% block header %}
<header class="intro-header" style="background-image: url('{{ STATIC_URL }}img/about-bg.jpg')">
<div class="container">
<div class="row">
<div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
<div class="page-heading">
<h1>{% placeholder "title" %}</h1>
<hr class="small">
<span class="subheading">{% placeholder "sub_title" %}</span>
</div>
</div>
</div>
</div>
</header>
{% endblock header %}
<!-- Main Content -->
<div class="container">
{% block content %}{% endblock content %}
</div>
<hr>
<!-- Footer -->
<footer>
<div class="container">
<div class="row">
<div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
<ul class="list-inline text-center">
<li>
<a href="https://twitter.com/alkutepov">
<span class="fa-stack fa-lg">
<i class="fa fa-circle fa-stack-2x"></i>
<i class="fa fa-twitter fa-stack-1x fa-inverse"></i>
</span>
</a>
</li>
<li>
<a href="https://www.facebook.com/alkutepov">
<span class="fa-stack fa-lg">
<i class="fa fa-circle fa-stack-2x"></i>
<i class="fa fa-facebook fa-stack-1x fa-inverse"></i>
</span>
</a>
</li>
<li>
<a href="https://github.com/AlexeyKutepov">
<span class="fa-stack fa-lg">
<i class="fa fa-circle fa-stack-2x"></i>
<i class="fa fa-github fa-stack-1x fa-inverse"></i>
</span>
</a>
</li>
</ul>
<p class="copyright text-muted">Copyright © {% static_placeholder "copyright" %}</p>
</div>
</div>
</div>
</footer>
<!-- jQuery -->
<script src="{{ STATIC_URL }}js/jquery.min.js"></script>
<!-- Bootstrap Core JavaScript -->
<script src="{{ STATIC_URL }}js/bootstrap.min.js"></script>
<!-- Theme JavaScript -->
<script src="{{ STATIC_URL }}js/clean-blog.min.js"></script>
{% render_block "js" %}
</body>
</html>
Я не буду подробно разбирать этот код, так как статья про DjangoCMS, а не про вёрстку. Тем более вы можете использовать любой другой шаблон вместо этого или создать свой собственный. Данный шаблон я брал «как есть» и дополнил его тегами Django и DjangoCMS, о которых мы сейчас и поговорим.
Как всегда в самом начале мы должны загрузить все необходимые шаблонные теги:
{% load cms_tags menu_tags sekizai_tags %}
В тег <title> я добавил блок title для того чтобы была возможность задавать разные заголовки страницы в разных шаблонах:
<title>{% block title %}Сайт Алексея Кутепова{% endblock title %}</title>
Далее загружаем таблицы стилей из папки static и нужные для шаблона шрифты из интернета:
<!-- Bootstrap Core CSS -->
<link href="{{ STATIC_URL }}css/bootstrap.min.css" rel="stylesheet">
<!-- Theme CSS -->
<link href="{{ STATIC_URL }}css/clean-blog.min.css" rel="stylesheet">
<!-- Custom Fonts -->
<link href="{{ STATIC_URL }}css/font-awesome.min.css" rel="stylesheet" type="text/css">
<link href='https://fonts.googleapis.com/css?family=Lora:400,700,400italic,700italic' rel='stylesheet' type='text/css'>
<link href='https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800' rel='stylesheet' type='text/css'>
{% render_block "css" %}
Обратите внимание на переменную {{ STATIC_URL }} — в место него Django автоматически подставляет путь до папки со статикой. Так же в конце присутствует тег {% render_block «css» %}, который на данном этапе бесполезен, но при усложнении шаблона может здорово помочь: вместо этого тега можно динамически подставлять таблицы стилей в зависимости от определённых условий.
После тега <body> нужно не забыть поставить шаблонный тег {% cms_toolbar %}, так как он отвечает за отображение верхней панели инструментов в режиме редактирования сайта. Как вы могли убедиться, мы её используем довольно часто и без неё у нас просто не будет возможности вносить изменения.
В блок навигации я добавил тег {% show_menu 0 100 100 100 %}, который отвечает за отображение пунктов меню (каждый пункт меню будет заключён в тег <li>):
<!-- Navigation -->
<nav class="navbar navbar-default navbar-custom navbar-fixed-top">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header page-scroll">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
<span class="sr-only">Меню</span>
Меню <i class="fa fa-bars"></i>
</button>
<a class="navbar-brand" href="/">Алексей Кутепов</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav navbar-right">
{% show_menu 0 100 100 100 %}
</ul>
</div>
<!-- /.navbar-collapse -->
</div>
<!-- /.container -->
</nav>
В шаблоне предусмотрен красивый хедер с фоновым изображением. Код, который отвечает за отображение хедера я заключил в блок header для того чтобы его можно было переопределить в дочерних шаблонах. Кроме того, в хедере у нас есть заголовок и подзаголовок, вместо которых я добавил плейсхолдеры title и sub_title, для того чтобы их можно было динамически изменять. При загрузке фонового изображения обязательно используется переменная {{ STATIC_URL }}:
{% block header %}
<header class="intro-header" style="background-image: url('{{ STATIC_URL }}img/about-bg.jpg')">
<div class="container">
<div class="row">
<div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
<div class="page-heading">
<h1>{% placeholder "title" %}</h1>
<hr class="small">
<span class="subheading">{% placeholder "sub_title" %}</span>
</div>
</div>
</div>
</div>
</header>
{% endblock header %}
После хедера идёт блок content, с которым вы знакомы по предыдущему шаблону:
<!-- Main Content -->
<div class="container">
{% block content %}{% endblock content %}
</div>
Далее идёт футер, в котором я использовал новый шаблонный тег — {% static_placeholder «copyright» %}. Основное отличие тега static_placeholder от placeholder заключается в том что содержимое static_placeholder наследуется дочерними шаблонами. В данном случае нам достаточно один раз задать значение {% static_placeholder «copyright» %} на главной странице и оно автоматически будет отображаться на других страницах нашего сайта:
<p class="copyright text-muted">Copyright © {% static_placeholder "copyright" %}</p>
В самом конце мы загружаем все необходимые скрипты из папки со статикой:
<!-- jQuery -->
<script src="{{ STATIC_URL }}js/jquery.min.js"></script>
<!-- Bootstrap Core JavaScript -->
<script src="{{ STATIC_URL }}js/bootstrap.min.js"></script>
<!-- Theme JavaScript -->
<script src="{{ STATIC_URL }}js/clean-blog.min.js"></script>
{% render_block "js" %}
Теперь чтобы новый шаблон отобразился корректно, нужно поместить все зависимости в папку django_cms_project/django_cms_example/static/, для этого просто скопируйте их из репозитория данного проекта на GitHub.
Перезагрузите сервер и обновите страницу в браузере. Если вы всё сделали правильно, то должны увидеть сайт уже в обновлённом дизайне:
Пока ещё не вся информация отображается на странице, так как мы ещё не заполнили новые плейсхолдеры. Нажмите синюю кнопку Редактировать страницу на верхней панели, а затем на кнопку Структура. Как вы видите, в структуре страницы появились 3 новых плейсхолдера: Title (у меня система перевела название этого плейсхолдера на русский язык как Заголовок), Sub_Title и Copyright.
Теперь нужно заполнить эти плейсхолдеры какой-либо информацией, для этого нажмите на кнопку + возле каждого из новых плейсхолдеров, в открывшемся окне выберите плагин Текст, после этого откроется редактор. Нажмите кнопку Источник в панели инструментов редактора и введите любой текст.
Кроме того у нас ещё есть плейсхолдер content (Содержимое), который мы заполняли ранее. Удалите из него плагин Google Map и откройте на редактирование плагин Текст. Затем нажмите кнопку Источник в редакторе и введите произвольный текст, заключённый в теги:
<div class="row">
<div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
<p>Произвольный текст</p>
</div>
</div>
Я заполнил плейсхолдеры вот так:
Нажмите кнопку Опубликовать изменения страницы и откроется вот такое окно уже с заполненной информацией:
Отлично! Теперь нас есть полноценная первая страница сайта. Давайте для примера создадим новый шаблон и сделаем ещё одну страницу. Предположим нам нужен особенный шаблон для раздела с контактами на сайте. Для этой цели создайте в папке templates файл contacts.html с таким содержимым:
{% extends "base.html" %}
{% load cms_tags %}
{% block title %}{% page_attribute "page_title" %}{% endblock title %}
{% block header %}
<header class="intro-header" style="background-image: url('{{ STATIC_URL }}img/contact-bg.jpg')">
<div class="container">
<div class="row">
<div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
<div class="page-heading">
<h1>{% placeholder "title" %}</h1>
<hr class="small">
<span class="subheading">{% placeholder "sub_title" %}</span>
</div>
</div>
</div>
</div>
</header>
{% endblock header %}
{% block content %}
<div class="row">
<div class="col-lg-10 col-lg-offset-2 col-md-10 col-md-offset-1">
<div class="col-lg-6 col-sm-12 maincontent">
<h3>Контакты</h3>
<br>
<h5><i class="fa fa-envelope-o" aria-hidden="true"></i> {% placeholder "email" %}</h5>
<h5><i class="fa fa-skype" aria-hidden="true"></i> {% placeholder "skype" %}<br></h5>
<h5><i class="fa fa-phone" aria-hidden="true"></i> {% placeholder "phone" %}<br></h5>
</div>
<div class="col-lg-6 col-sm-12 maincontent">
<h3>Я в социальных сетях</h3>
<br>
<h5><i class="fa fa-vk" aria-hidden="true"></i> {% placeholder "vk" %}<br></h5>
<h5><i class="fa fa-facebook" aria-hidden="true"></i> {% placeholder "facebook" %}<br></h5>
<h5><i class="fa fa-linkedin" aria-hidden="true"></i> {% placeholder "linkedin" %}<br></h5>
<h5><i class="fa fa-github" aria-hidden="true"></i> {% placeholder "github" %}<br></h5>
</div>
</div>
</div>
{% endblock content %}
В этом коде нет ничего нового, я просто сверстал список контактов и добавил для каждого контакта свой плейсхолдер. Ещё я для красоты переопределил блок header. Теперь нужно добавить этот шаблон в settings.py:
CMS_TEMPLATES = (
('fullwidth.html', 'Fullwidth'),
('sidebar_left.html', 'Sidebar Left'),
('sidebar_right.html', 'Sidebar Right'),
('contacts.html', 'Contacts')
)
Теперь перезагрузите сервер и создайте новую страницу, выбрав в верхней панели Страница > Создать страницу > Новая страница:
Заполните все поля в появившемся окне и нажмите кнопку Сохранить и продолжить редактирование:
В новом окне нажмите кнопку Расширенные настройки и выберите шаблон Contacts из выпадающего списка:
Нажмите синюю кнопку Сохранить и вы будете автоматически перенаправлены в раздел Структура новой страницы. Необходимо заполнить все плейсхолдеры используя плагины Текст и Ссылка:
Прежде чем пойти дальше, хочу рассказать о плагине Ссылка. Вот так выглядит окно с настройками плагина:
Основные поля:
- DISPLAY NAME — отображаемое название ссылки
- EXTERNAL LINK — используется в том случае, если ссылка идёт на внешний ресурс
- INTERNAL LINK — позволяет ссылаться на внутренние страницы сайта
- Link settings — в этом разделе для нас интерес представляют поля, которые позволяют сформировать ссылку для электронной почты и телефона
Как видите, сложного ничего нет. Плагин простой, удобный в использовании и довольно функциональный. Я уверен что вы без труда сможете им пользоваться.
Теперь вернёмся к нашей новой странице. После заполнения всех плейсхолдеров необходимо нажать синюю кнопку Опубликовать страницу немедленно в верхней панели инструментов. Все внесённые изменения на странице будут сохранены, сама страница опубликована и в главном меню сайта появится новый пункт Контакты. Вот что должно получиться в итоге:
У нас получился небольшой и красивый сайт, но так как данный проект демонстрационный, тут можно ещё много чего доработать. Теперь вы знаете основы работы с DjangoCMS и уже можете создать свой собственный сайт на основе данного фреймворка.
В этой статье я затронул только малую часть возможностей DjangoCMS, поэтому будет продолжение, где я расскажу о плагинах, сторонних дополнениях и кастомизации функционала сайта.
Исходники проекта вы можете найти по ссылке (не забудьте переключиться на ветку part1): https://github.com/AlexeyKutepov/django_cms_project
На чтение 5 мин Просмотров 1.2к. Опубликовано 28.04.2021
Системы управления содержимым (CMS) предоставляют настольное или веб-программное обеспечение для управления вашим цифровым контентом. Термин «цифровой контент» здесь обычно относится к общедоступным или частным веб-сайтам, веб-приложениям или другому цифровому / мультимедийному контенту, обслуживаемому через удаленные или локальные серверы. Без надлежащей CMS может быть сложно создавать, управлять и организовывать контент для ваших веб-проектов, особенно если ваш проект очень большой, вы публикуете много контента и регулярно сотрудничаете с другими.
В своей наиболее распространенной форме CMS предоставляет графические утилиты, которые запускаются в веб-браузерах, хотя также существуют некоторые приложения для командной строки и настольные приложения. Эти графические утилиты могут включать в себя панель администратора, редакторы страниц / сообщений, редакторы галереи, инструменты для загрузки и скачивания мультимедиа, инструменты для управления учетными записями пользователей, комментарии, платежи, формы, инструменты SEO, инструменты управления URL-адресами, инструменты управления базами данных и интерфейс для изменения. почти все аспекты вашего веб-сайта / веб-приложения. Существует множество готовых, но хорошо расширяемых CMS, которые предоставляют многочисленные инструменты для управления вашим цифровым контентом, например WordPress (на основе PHP). Некоторые предприятия разрабатывают свои собственные частные CMS с нуля, предназначенные для использования только внутри организации, и эти CMS никогда не публикуются.
Содержание
- Django CMS
- Wagtail
- Plone
- Lektor
- Superdesk
- Заключение
Django CMS
Django CMS — одна из наиболее широко используемых и популярных CMS на базе Python, доступных сегодня. Основанная на среде веб-разработки Django, Django CMS предоставляет исчерпывающую документацию, а сообщество предоставляет обширную поддержку. По сути, основная цель Django CMS — предоставить безопасные и быстрые инструменты управления контентом, которые экономят ваше время.
Некоторые из основных функций Django CMS включают в себя множество предустановок для различных нужд управления контентом, индивидуальный брендинг и темы, интеграцию с приложениями CRM, официальные и сторонние пакеты и плагины для расширения функциональности, интерфейс для редактирования и управления контентом, перетаскивание поддержка, многоязычная поддержка и т. д.
Wagtail
Wagtail — еще одна популярная CMS, основанная на фреймворке веб-разработки Django. Она относительно новее, чем Django CMS, активно развивается и имеет постоянно растущее сообщество. Как и Django CMS, Wagtail также стремится предоставить безопасные инструменты, которые помогают в быстрой разработке и упрощении создания контента. Основные функции Wagtail включают инструменты для интеграции с приложениями CRM, графический интерфейс для создания, редактирования и управления контентом, современную панель администратора, интегрированные инструменты поиска, предварительный просмотр в реальном времени, встроенную систему контроля версий, инструменты управления несколькими сайтами, интеграцию с другими Приложения на основе Django, генератор статических сайтов, сторонние плагины и модули и т.д.
Plone
Plone — это CMS с открытым исходным кодом, основанная на сервере веб-приложений Python и Zope. Он разрабатывался более двух десятилетий и лучше всего подходит для управления контентом для веб-сайтов / веб-приложений корпоративного уровня. Хотя как частное лицо вы можете использовать Plone для управления простыми блогами или хобби-сайтами, его предложение может быть не таким надежным, как другие упомянутые в этой статье CMS для управления контентом для личных блогов. Основное внимание Plone уделяет созданию CMS корпоративного уровня. Основные особенности Plone включают в себя серьезное внимание к безопасности корпоративного уровня, управлению медиафайлами, инструментам доступности, управлению пользователями, контролю разрешений, многоязыковой поддержке и REST API для управления контентом из ваших пользовательских программ.
Lektor
Lektor — это генератор статических сайтов и CMS, объединенные в одно целое. Он поддерживает структуру «плоских файлов», где каждая часть данных хранится в новых или существующих файлах в древовидной структуре. Lektor не использует какую-либо базу данных SQL для управления контентом. Он уделяет много внимания правильному макету и структуре всего содержимого на основе файлов, что помогает в лучшей организации файлов проекта и улучшенном управлении версиями. Lektor поставляется с графической веб-панелью администратора для управления и редактирования вашего контента.
Superdesk
Superdesk — это «безголовая» CMS на основе Python, специально разработанная для журналистов и организаций, публикующих новости в цифровом формате. Термин «безголовый» здесь относится к особому типу CMS, который предоставляет внутренний интерфейс, API-интерфейсы и инструменты командной строки для создания и управления контентом вашего веб-проекта, фактически не предоставляя вам инструментов для отображения данных на веб-сайте HTML. Чтобы отобразить веб-сайт, вам необходимо получить данные через его API, написать собственный интерфейс с нуля или использовать сторонний сервис, который может предоставить интерфейс для создания новых веб-страниц, подключившись к API вашей автономной CMS. Superdesk в основном ориентирован на обеспечение серверной части, которая облегчает производство и распространение новостей. Обратите внимание, что Superdesk также предлагает платную поддержку и услуги, но версия с открытым исходным кодом полностью бесплатна.
Заключение
Это единственные основные бесплатные CMS на основе Python с открытым исходным кодом, которые регулярно разрабатываются и предоставляют хорошую документацию и поддержку сообщества. Некоторые другие программы CMS на основе Python, которые раньше находились в разработке, были заархивированы или переведены в режим обслуживания.
The django CMS is an easy to use, open-source content management build using Django/Python. It is a great option for new developers who wish to create a site that tailors to their needs. The user-friendly interface is aimed at developers and content editors. There are plenty of addons in the Django marketplace created by a live active community of developers.The setup is incredibly simple, and in this django CMS tutorial, we’ll take you through the first five steps to help get you started.
What you need to get started
First of all, you don’t need to be a senior developer or have prior experience as a developer with Django or Python to create your first django CMS demo website. The added benefit of django CMS, is it’s free.
Before we begin the django CMS tutorial, you will need to know that there are three ways to install django CMS for free.
-
You can either set up a project on Divio Cloud, which is fast and useful for people without a technical background and a good starting point to experience the CMS User Interface.
-
As another option, you can set up the project using docker. It is a good way for a developer locally without an external vendor and we use this option in this django CMS demo.
-
The last option is to install django CMS manually by using virtualenv. This option is a good way for developers that want to install everything by hand to understand better and have full control.
For the sake of this demonstration we will use Option 2, please read on.
Step 1: Set up docker
Install docker from here: https://docs.docker.com/get-docker/
Step 2: Run the demo project in docker
Info: The demo project is a minimal django project with some additional requirements in the requirements.txt.
Open the terminal application on your computer and go to a safe folder (i.e. cd ~/Projects
), then:
git clone [email protected]:django-cms/django-cms-quickstart.git
cd django-cms-quickstart
docker compose build web
docker compose up -d database_default
docker compose run web python manage.py migrate
docker compose run web python manage.py createsuperuser
docker compose up -d
Open your browser and insert http://localhost:8000/; there you should be invited to login and continue with Step 3: create your first page.
By the way, django CMS has no license costs! So before you create your first page, please take a moment to learn more about how the django CMS project is organized and who is funding it.
django CMS is funded by the community
django CMS is a content management system used by thousands of website owners, developers, businesses and content editors. Without the support of our sponsors, partners and users like you, django CMS would not be possible.
django CMS is open source and has no license costs. It relies on users like you to contribute to its development, support and operation.
Thanks to our great team of volunteers and the members of the django CMS Association, the django CMS project can be run. For that, we are forever grateful!
Are you also a fan of django CMS and want to support it? Join fhe django CMS Association and receive exclusive membership benefits
Support django CMS
Step 3: Create your first page
- Once you login you can press Create on the top right.
- Then you will see a pop-up window where the “New page” is marked blue.
- Press New Page and select Next.
After selecting Next, you will add in your title and some basic text content for the new page, click Create.
Here is your newly created page.
Step 4: Publish your page
The page we just created is just a draft and needs to be published once you finish. As an editor, only you can see and edit your drafts, other visitors to your site will only see your published pages.
Press «Publish page now.»
To edit the page, you can switch back into editing mode using the «Edit» button, and return to the published version of the page using the «view published» button.
In the editing mode, you can double-click on the paragraph of the text to change it, add formatting, and save it again. Any changes that are made after publishing are saved to a draft and will not be visible until you re-publish.
Extend your django CMS project with plugins
Here’s a list of all recommended plugins and repositories under the django CMS umbrella.
Continue learning django CMS with our documentation
Further Reading
Are you interested in learning more from django CMS tutorials? Then have a look at the django vs laravel blog post from what.digital where you can read an in-depth comparison on which web framework to choose.
blog comments powered by