Как правильно ограничить длину текста в CKEditor 5 без учёта HTML-разметки

Как правильно ограничить длину текста в CKEditor 5 без учёта HTML-разметки

При использовании CKEditor 5 в Django-проектах многие сталкиваются с проблемой: стандартный параметр `max_length` считает длину HTML-кода, а не видимого текста. 

Рассмотрим решение этой проблемы.

Стандартное определение поля:

content_short = CKEditor5Field(
    max_length=600,  # Считает ВСЕ символы, включая HTML-теги
    verbose_name='Краткий текст'
)

Для решения проблемы с подсчётом длины текста без HTML-разметки в CKEditor5Field предлагаю следующие доработки:

Пример:

  • Видимый текст: "Привет, мир!" (12 символов)

  • HTML-код: <p><strong>Привет</strong>, мир!</p> (32 символа)

Решение

1. Валидация на уровне модели

from django.core.exceptions import ValidationError
from django.utils.html import strip_tags

class Article(models.Model):
    content_short = CKEditor5Field(
        max_length=2000,  # Лимит с запасом для HTML
        blank=True,
        null=True,
        verbose_name='Краткий текст',
    )

    def clean(self):
        super().clean()
        if self.content_short:
            clean_text = strip_tags(self.content_short)
            if len(clean_text) > 600:
                raise ValidationError(
                    f'Максимум 600 символов. Сейчас: {len(clean_text)}'
                )

2. JavaScript-счетчик для админки

static/js/ckeditor_counter.js:

document.addEventListener('DOMContentLoaded', () => {
    document.querySelectorAll('.django-ckeditor-widget').forEach(widget => {
        const counter = document.createElement('div');
        counter.className = 'ckeditor-counter';
        widget.appendChild(counter);
        
        const editor = widget.querySelector('.ck-editor__editable');
        const update = () => {
            const text = editor.innerText;
            counter.textContent = `${text.length}/600 символов`;
            counter.style.color = text.length > 600 ? 'red' : '#666';
        };
        
        editor.addEventListener('input', update);
        update();
    });
});

Подключение в админке:

class Media:
    css = {'all': ('css/ckeditor_counter.css',)}
    js = ('js/ckeditor_counter.js',)

3. Валидация в формах

class ArticleForm(forms.ModelForm):
    def clean_content_short(self):
        content = self.cleaned_data['content_short']
        if content and len(strip_tags(content)) > 600:
            raise forms.ValidationError("Превышен лимит в 600 символов")
        return content

Полная реализация

  1. Модель - оставляем max_length с запасом для HTML

  2. Валидация - проверяем чистый текст через strip_tags()

  3. Интерфейс - JS-счетчик для наглядности

  4. Защита - валидация на всех уровнях (клиент/сервер)

Альтернативные подходы

  1. Кастомное поле:

class CleanTextLengthField(CKEditor5Field):
    def __init__(self, *args, clean_length=600, **kwargs):
        self.clean_length = clean_length
        super().__init__(*args, **kwargs)

    def clean(self, value, model_instance):
        clean_text = strip_tags(value)
        if len(clean_text) > self.clean_length:
            raise ValidationError(...)
        return super().clean(value, model_instance)
  1. Плагин CKEditor - можно создать кастомный плагин для подсчёта символов.

Вывод

Предложенное решение обеспечивает:

  • Корректный подсчёт только видимого текста

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

  • Надёжную валидацию на сервере

  • Сохранение всех возможностей CKEditor

Для реализации достаточно скопировать код из примеров и адаптировать под свой проект.

### Советы по публикации:

1. Добавьте скриншоты интерфейса с примером работы
2. Включите раздел "Частые вопросы"
3. Добавьте примеры ошибок и их решений
4. Упомяните вашу версию Django и django-ckeditor

Категория: Программирование | автор: fominyh_vv

Опубликовано: 18-04-2025 15:12