Django - CKEDitor Tutorial (Python)

How to add rich text (WYSIWYG) editing to Django forms.

Updated May 25, 2024

Making Money with Django and AI: How to Build SaaS Services Using Python

Learn how to build AI-driven websites with the Django web framework and monetize your services.
Read More
You will receive the book in PDF and ePub formats.

Table of contents


Webpage content editors want an intuitive interface to create rich HTML content. This is where tools like CKEditor come in handy. Personally, I use Markdown to create content such as blog posts, but it might not be the best option for non-techy end users.

Note: This example uses the free CKEditor 4, which is considered end-of-life software. Can you continue using CKEditor 4? Yes, but you need to be aware of the possible security vulnerabilities. There are no new security patches or bug fixes coming out. CKEditor 4 LTS (which provides essential security fixes) and CKEditor 5 are available as paid products.

You will learn

  • How to add rich text using a WYSIWYG editor.
  • How to upload files inside the editor.
  • How to customize the toolbar.
  • How to add syntax-highlighted code snippets.


Add these lines to the project settings file:

    # start
    # end

# start
MEDIA_URL = '/media/' 
MEDIA_ROOT = 'media/'
# end
  • User-uploaded files will be stored in the uploads directory inside the media folder.


Add RichTextUploadingField to a model:

from ckeditor_uploader.fields import RichTextUploadingField

class Post(models.Model):
    body = RichTextUploadingField(blank=True)
  • RichTextUploadingField allows rich text content and file uploads directly within the text editor.


Edit the project URL configuration file and include CKEditor URLs:

urlpatterns = [
    path('ckeditor/', include(
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
  • The static function is used to serve uploaded media files during development.


Use the safe filter in templates when outputting rich text content:

<div class="body">{{ post.body|safe }}</div>
  • The safe filter bypasses automatic escaping. You can use it when you have content that you know is from a trusted source or has been sanitized.
  • You can use a library like html-sanitizer to sanitize HTML.

Create some rich content:

This is how the output HTML might look like:

<div class="body">
    <h2>Lorem ipsum</h2>
        <img alt="" src="/media/uploads/2024/12/30/example.jpg" style="height:62px; width:100px">
    <p>Lorem ipsum dolor...</p>

Toolbar customization

Edit and add the following setting:

        {'toolbar': 'Custom', 
         'toolbar_Custom': [
            ['Bold', 'Link', 'Unlink', 'Image'], 

Now, users can only make text bold, add or remove links, and insert images:

You can define multiple configurations like this:

        {'toolbar': 'Custom',
         'toolbar_Custom': [
             ['Bold', 'Link', 'Unlink', 'Image'],
        {'toolbar': 'Special', 'height': 500,

The special editor can only make texts bold and its default height is 500 px. Specify the configuration using the config_name argument:

class Post(models.Model):
    body = RichTextUploadingField(blank=True, config_name='special')

Note: you can find the default Full toolbar configuration in the CKEditor package file:


Code snippets

The CKEditor package includes plugins that you might want to use. One of them is the codesnippet plugin. This allows us to add code snippets. Enable it like this:

        {'toolbar': 'Custom',
         'toolbar_Custom': [
             ['Bold', 'Link', 'Unlink', 'Image'],
        {'toolbar': 'Special', 'height': 500,
                 ['CodeSnippet'], # here
             ], 'extraPlugins': 'codesnippet', # here

Click the CodeSnippet button in the toolbar and add a snippet:

You can use a library such as highlight.js for syntax highlighting:

    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <!-- highlight.js-->
    <link rel="stylesheet" href="//">
    <script src="//"></script>
    <!-- highlight.js -->


In this case we are using the Atom One Dark theme to style the code:

This is how the final HTML looks like:

    <code class="language-python hljs">
        <span class="hljs-function">
            <span class="hljs-keyword">def</span> 
            <span class="hljs-title">hello_world</span>():
        print(<span class="hljs-string">"Hello, World!"</span>)

Leave a comment

You can use Markdown to format your comment.