Как написать cms на python

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 (это самая актуальная версия на момент написания статьи, советуем всегда выбирает самые свежие версии).

Создание сайта на Wagtail (CMS на Django)

Если вы профи в Linux, то можете выбрать настройку «Без панели» при выборе панели управления, но если вы хотите иметь некий интерфейс настроек, то советуем выбрать панель управления «Vesta CP«.

Создание сайта на Wagtail (CMS на Django)

Подключаемся по SSH и выполняем необходимые команды в консоли.

$ apt update

$ apt upgrade

На VPS у меня уже был установлен Python 3.6.7 но если по каким либо причинам его у вас нет, то устанавливаем его таким образом:

Далее, устанавливаем необходимые библиотеки:

$ apt install python3setuptools python3dev python3venv

$ apt install libtiff5dev libjpeg8dev zlib1gdev

$ apt install libfreetype6dev liblcms2dev libwebpdev tcl8.6dev tk8.6dev pythontk

Не забываем про pip:

$ apt install python3pip

Все манипуляции и установки через pip выполняем исключительно в виртуальном окружении, чтобы не засорять ненужными библиотеками главное окружение. В папке /home создаем новую папку /home/web

Находясь в папке /home/web мы создадим виртуальное окружение для нашего приложения:

python3 m venv venv

source venv/bin/activate

Вводная строка терминала изменилась на что-то подобное:

(venv) user@kvmde5814996:/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 покажет страницу приветствия:

Создание сайта на Wagtail (CMS на Django)Вы можете перейти к административному разделу в http://127.0.0.1:8000/admin

Создание сайта на Wagtail (CMS на Django)

Расширяем модель домашней страницы

Изначально, приложение “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 %}templatehomepage{% endblock %}

{% block content %}

    {{ page.body|richtext }}

{% endblock %}

Создание сайта на Wagtail (CMS на Django)

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 %}templateblogindexpage{% 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 %}templateblogpage{% 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 (CMS на Django)

Создание сайта на Wagtail (CMS на Django)

Wagtail дает вам полный контроль над тем, какой тип содержимого может быть создан под различными родительскими типами контента. По умолчанию, любая страница может быть дочерней любого типа страницы.

Создание сайта на Wagtail (CMS на Django)

Теперь у вас в распоряжении базовый рабочий блог. Перейдите по URL /blog и увидите что-нибудь вроде следующего:

Создание сайта на Wagtail (CMS на Django)

Заголовки должны переводить к страницам постов, а ссылка на главную страницу должна появиться в футере каждой страницы поста.

Дочерние и родительские элементы

Большая часть работы, выполняемая в 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()

Переопределение контекста

Есть небольшие проблемы с главной страницей нашего блога:

  1. Контент в блогах (как правило) показывается в обратном хронологическом порядке;
  2. Нам нужно быть уверенными в том, что мы показываем только опубликованный контент (без черновиков).

Чтобы достичь этих целей, нам нужно сделать нечто большее, чем просто взять дочерние элементы страниц индекса в шаблоне.

Вместо этого, нам нужно будет обновить 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 %}templateblogpage{% 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.

Создание сайта на Wagtail (CMS на Django)

Так как изображения нашей галереи являются объектами базы данных сами по себе, мы можем запрашивать и повторно использовать их, вне зависимости от тела поста в блоге. Давайте определим метод 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, и вы сможете отмечать посты:

Создание сайта на Wagtail (CMS на Django)

Для отображение тегов в 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 , как и модель профиля для авторов — оставим это как практическое задание для читателя.

Нажатие на кнопку тега внизу поста теперь должно открывать страницу следующим образом:

Создание сайта на Wagtail (CMS на Django)

Категории

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

Сначала, мы определим модель 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 %}

Создание сайта на Wagtail (CMS на Django)

Являюсь администратором нескольких порталов по обучению языков программирования Python, Golang и Kotlin. В составе небольшой команды единомышленников, мы занимаемся популяризацией языков программирования на русскоязычную аудиторию. Большая часть статей была адаптирована нами на русский язык и распространяется бесплатно.

E-mail: vasile.buldumac@ati.utm.md

Образование
Universitatea Tehnică a Moldovei (utm.md)

  • 2014 — 2018 Технический Университет Молдовы, ИТ-Инженер. Тема дипломной работы «Автоматизация покупки и продажи криптовалюты используя технический анализ»
  • 2018 — 2020 Технический Университет Молдовы, Магистр, Магистерская диссертация «Идентификация человека в киберпространстве по фотографии лица»

Python application

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 to True in your hosting environment.
  • For production environment (if DEBUG is false) django requires you to whitelist the domain. Set the env var DOMAIN 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 &copy; {% 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 &copy; {% 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 никогда не публикуются.

Содержание

  1. Django CMS
  2. Wagtail
  3. Plone
  4. Lektor
  5. Superdesk
  6. Заключение

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. 

  1. 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. 

  2. 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.

  3. 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. 

CMS for developers and content editors - The new page tree in django CMS version 3.3

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.
     

django CMS create your first page

After selecting Next, you will add in your title and some basic text content for the new page, click Create.
 

how to create a page with django CMS

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.»

django CMS tutorial

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

Понравилась статья? Поделить с друзьями:
  • Как написать cmake файл
  • Как написать cleo скрипт для gta san andreas
  • Как написать christmas cards
  • Как написать case study
  • Как написать call to action