Интеграция WYSIWYG-редактора, установка CKEditor
Визуальный редактор
Писать статью в голом <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
Замените поле полного описания статьи на поле редактора:
full_description = CKEditor5Field("Полное описание", config_name="extends") из django_ckeditor_5.fields.
from django_ckeditor_5.fields import CKEditor5Field
class Article(models.Model):
full_description = CKEditor5Field("Полное описание", config_name="extends")
Подключите маршруты редактора (нужны для загрузки картинок): в backend/urls.py добавьте
path("ckeditor5/", include("django_ckeditor_5.urls")).
urlpatterns = [
path("admin/", admin.site.urls),
path("ckeditor5/", include("django_ckeditor_5.urls")),
]
Почему для нового проекта берут django-ckeditor-5, а не старый пакет django-ckeditor?
Текст статьи из редактора содержит HTML. Как вывести его в шаблоне, чтобы теги не экранировались?
Очистите HTML перед сохранением статьи с помощью nh3: в методе save модели выполните
self.full_description = nh3.clean(self.full_description) и вызовите super().save(*args, **kwargs).
import nh3
class Article(models.Model):
def save(self, *args, **kwargs):
self.full_description = nh3.clean(self.full_description)
super().save(*args, **kwargs)
Войдите, чтобы отмечать пройденные уроки.