Интеграция WYSIWYG-редактора, установка CKEditor

⏱ 25 мин · 🧩 заданий: 5

Визуальный редактор

Писать статью в голом <textarea> неудобно: ни заголовков, ни списков, ни картинок. WYSIWYG-редактор («что видишь, то и получишь») даёт панель форматирования и сохраняет результат как HTML.

Самый известный — CKEditor. Для Django есть два пакета:

Пакет Статус
django-ckeditor Устарел: основан на CKEditor 4, у которого закончилась бесплатная поддержка безопасности
django-ckeditor-5 Актуальный, основан на CKEditor 5

Используем django-ckeditor-5.

Установка

pip install django-ckeditor-5
INSTALLED_APPS = [
    ...
    "django_ckeditor_5",
]
# backend/urls.py
urlpatterns = [
    path("admin/", admin.site.urls),
    path("ckeditor5/", include("django_ckeditor_5.urls")),
    ...
]

Маршрут нужен для загрузки картинок из редактора. Файлы попадают в MEDIA_ROOT — в разработке раздавайте медиа через static() в urls.py, как для аватаров.

Настройки

Минимальная конфигурация в settings.py:

CKEDITOR_5_CONFIGS = {
    "default": {
        "toolbar": ["heading", "|", "bold", "italic", "link", "bulletedList", "numberedList", "blockQuote", "imageUpload"],
    },
    "extends": {
        "toolbar": [
            "heading", "|", "bold", "italic", "underline", "strikethrough", "link", "|",
            "bulletedList", "numberedList", "blockQuote", "|",
            "code", "codeBlock", "insertTable", "imageUpload", "mediaEmbed", "|",
            "undo", "redo",
        ],
        "codeBlock": {
            "languages": [
                {"language": "python", "label": "Python"},
                {"language": "html", "label": "HTML"},
                {"language": "javascript", "label": "JavaScript"},
            ],
        },
        "image": {"toolbar": ["imageTextAlternative", "|", "imageStyle:alignLeft", "imageStyle:alignRight"]},
        "table": {"contentToolbar": ["tableColumn", "tableRow", "mergeTableCells"]},
    },
}

CKEDITOR_5_FILE_STORAGE = "django.core.files.storage.FileSystemStorage"
CKEDITOR_5_UPLOAD_FILE_TYPES = ["jpeg", "jpg", "png", "gif", "webp"]
CKEDITOR_5_FILE_UPLOAD_PERMISSION = "authenticated"   # загружать могут только вошедшие

Поле модели

from django_ckeditor_5.fields import CKEditor5Field


class Article(models.Model):
    ...
    full_description = CKEditor5Field("Полное описание", config_name="extends")

В базе это обычный текст — миграция не меняет тип столбца. В админке поле сразу появится с редактором.

Редактор в пользовательской форме

В формах создания и редактирования статьи нужен виджет и подключение статики редактора:

from django_ckeditor_5.widgets import CKEditor5Widget


class ArticleCreateForm(forms.ModelForm):
    class Meta:
        model = Article
        fields = (...)
        widgets = {
            "full_description": CKEditor5Widget(config_name="extends"),
        }

В шаблоне формы выведите медиа формы (скрипты и стили редактора):

{% block content %}
  {{ form.media }}
  <form method="post" enctype="multipart/form-data">
    ...
  </form>
{% endblock %}

Обязательное поле и скрытый textarea

CKEditor прячет исходный <textarea>. Если у него атрибут required, браузер не сможет показать подсказку и форма «молча» не отправится. Сделайте self.fields["full_description"].required = False в __init__ формы и проверяйте пустоту в clean_full_description().

Вывод HTML и безопасность

Django экранирует всё, что выводит в шаблон. HTML из редактора показываем фильтром safe:

<div class="article-content">{{ article.full_description|safe }}</div>

Но safe означает «я доверяю этому HTML». Если статью пишет любой зарегистрированный пользователь, он может вставить <script> или <img onerror="..."> в обход редактора — отправив POST руками. Поэтому HTML очищаем на сервере перед сохранением. Подойдёт библиотека nh3:

pip install nh3
import nh3


class Article(models.Model):
    ...

    def save(self, *args, **kwargs):
        self.full_description = nh3.clean(self.full_description)
        if not self.slug:
            self.slug = unique_slugify(self, self.title)
        super().save(*args, **kwargs)

nh3.clean() оставляет безопасные теги (p, strong, a, img, table…) и удаляет script, style, обработчики on* и ссылки javascript:. Список разрешённого можно расширить параметрами tags и attributes.

Короткое описание для карточек — без HTML:

{{ article.short_description|striptags|truncatewords:30 }}

Итог

  • WYSIWYG-редактор — django-ckeditor-5 (старый django-ckeditor устарел).
  • CKEditor5Field в модели, CKEditor5Widget и {{ form.media }} в формах, маршрут ckeditor5/ для картинок.
  • Вывод — |safe, но только после очистки HTML на сервере (nh3).

Практика решено 0 из 5

Вы не вошли — ответы проверяются, но прогресс не сохранится. Войти или зарегистрироваться.
Задание 1 Написать код

Замените поле полного описания статьи на поле редактора: full_description = CKEditor5Field("Полное описание", config_name="extends") из django_ckeditor_5.fields.

Задание 2 Написать код

Подключите маршруты редактора (нужны для загрузки картинок): в backend/urls.py добавьте path("ckeditor5/", include("django_ckeditor_5.urls")).

Задание 3 Один вариант ответа

Почему для нового проекта берут django-ckeditor-5, а не старый пакет django-ckeditor?

Задание 4 Один вариант ответа

Текст статьи из редактора содержит HTML. Как вывести его в шаблоне, чтобы теги не экранировались?

Задание 5 Написать код

Очистите HTML перед сохранением статьи с помощью nh3: в методе save модели выполните self.full_description = nh3.clean(self.full_description) и вызовите super().save(*args, **kwargs).

Войдите, чтобы отмечать пройденные уроки.