Django CKEditor
django-ckeditor integrates the CKEditor 4 WYSIWYG rich text editor into Django projects, providing `RichTextField`, `RichTextUploadingField`, `CKEditorWidget`, and `CKEditorUploadingWidget` for seamless content management in both the admin interface and custom forms. The library is currently at version 6.7.3 and follows Django's release cycle for compatibility updates, though it has an important deprecation warning regarding its bundled CKEditor 4 version.
Warnings
- breaking Version 6.4.0 dropped support for Python versions older than 3.8 and Django versions older than 3.2. Ensure your project meets these minimum requirements before upgrading.
- breaking In version 6.0.0, the `ugettext_lazy()` function was replaced with `gettext_lazy()`. If you have custom translations or widget templates that directly use `ugettext_lazy()`, they will break.
- deprecated django-ckeditor bundles CKEditor 4.22.1, which is no longer officially supported by CKSource and has known unfixed security issues. Users are strongly advised to consider switching to CKEditor 5 (using `django-ckeditor-5`) or the non-free CKEditor 4 LTS package for security and continued support.
- gotcha When using `RichTextUploadingField` or `CKEditorUploadingWidget`, you must include `'ckeditor_uploader'` in `INSTALLED_APPS` and add `path('ckeditor/', include('ckeditor_uploader.urls'))` to your project's `urls.py`. Failing to do so will result in broken image/file upload functionality and a 404 error when accessing upload URLs.
- gotcha Customizing the CKEditor widget template (e.g., `ckeditor/widget.html`) might require adjustments with version 6.4.0 and later due to changes in the widget's context, deviating less from standard Django widget contexts.
Install
-
pip install django-ckeditor
Imports
- RichTextField
from ckeditor.fields import RichTextField
- RichTextUploadingField
from ckeditor_uploader.fields import RichTextUploadingField
- CKEditorWidget
from ckeditor.widgets import CKEditorWidget
- CKEditorUploadingWidget
from ckeditor_uploader.widgets import CKEditorUploadingWidget
Quickstart
import os
from django.db import models
from django.forms import ModelForm
from django.shortcuts import render, redirect
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static
# --- settings.py additions ---
# INSTALLED_APPS = [
# # ... other apps
# 'ckeditor',
# 'ckeditor_uploader',
# ]
#
# MEDIA_URL = '/media/'
# MEDIA_ROOT = os.path.join(BASE_DIR, 'media') # Ensure BASE_DIR is defined
# CKEDITOR_UPLOAD_PATH = 'uploads/'
# CKEDITOR_CONFIGS = {
# 'default': {
# 'toolbar': 'full',
# 'height': 300,
# 'width': '100%',
# 'extraPlugins': 'codesnippet',
# },
# }
#
# # --- urls.py additions (project level) ---
# # from django.urls import include, path
# # from django.conf import settings
# # from django.conf.urls.static import static
# #
# # urlpatterns = [
# # # ... other urls
# # path('ckeditor/', include('ckeditor_uploader.urls')),
# # ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
# --- app/models.py ---
from ckeditor_uploader.fields import RichTextUploadingField
class Article(models.Model):
title = models.CharField(max_length=200)
content = RichTextUploadingField(blank=True, null=True)
def __str__(self):
return self.title
# --- app/forms.py ---
class ArticleForm(ModelForm):
class Meta:
model = Article
fields = ['title', 'content']
# --- app/views.py ---
def create_article(request):
if request.method == 'POST':
form = ArticleForm(request.POST)
if form.is_valid():
form.save()
return redirect('article_list')
else:
form = ArticleForm()
articles = Article.objects.all()
return render(request, 'app/article_form.html', {'form': form, 'articles': articles})
def article_list(request):
articles = Article.objects.all()
return render(request, 'app/article_list.html', {'articles': articles})
# --- app/urls.py (within your app) ---
# urlpatterns = [
# path('create/', create_article, name='create_article'),
# path('', article_list, name='article_list'),
# ]
# --- app/templates/app/article_form.html ---
# {% extends 'base.html' %}
# {% block content %}
# <h2>Create Article</h2>
# <form method="post">
# {% csrf_token %}
# {{ form.media }} {# IMPORTANT: Renders CSS/JS for CKEditor #}
# {{ form.as_p }}
# <button type="submit">Save</button>
# </form>
# <hr/>
# <h2>Articles</h2>
# <ul>
# {% for article in articles %}
# <li>{{ article.title }}</li>
# {% endfor %}
# </ul>
# {% endblock %}
# --- app/templates/app/article_list.html ---
# {% extends 'base.html' %}
# {% block content %}
# <h2>Articles</h2>
# <ul>
# {% for article in articles %}
# <li><a href="#">{{ article.title }}</a></li>
# <div>{{ article.content|safe }}</div> {# Use |safe to render HTML #}
# {% empty %}
# <li>No articles yet. <a href="{% url 'create_article' %}">Create one</a>.</li>
# {% endfor %}
# </ul>
# {% endblock %}