Loading...

Enhancing Django Admin with CKEditor

06 Jul 2025
Apurv Chaudhary
django, blog, python, js
Enhancing Django Admin with CKEditor

Enhancing Django Admin with CKEditor for Rich Blog Content

Introduction

In this blog post, I'll walk you through how I added django-ckeditor to my Django project to create a powerful blog editor in the Django admin interface. CKEditor is a WYSIWYG (What You See Is What You Get) editor that allows content creators to format text, add images, and even include code snippets without knowing HTML.

Why CKEditor?

  • Rich Text Editing: Provides formatting options like bold, italic, lists, etc.
  • Media Integration: Easily embed images and other media
  • Code Snippets: Perfect for technical blogs with syntax highlighting
  • Customizable: Tailor the editor to your specific needs
  • User-Friendly: Makes content creation accessible to non-technical users

Installation

First, I installed django-ckeditor using pip:

pip install django-ckeditor
Then I added it to my requirements.txt file:
django-ckeditor==6.7.0
django-js-asset==2.2.0  # Required dependency
 

Configuration in settings.py

After installation, I added 'ckeditor' to my INSTALLED_APPS in settings.py:

INSTALLED_APPS = [
    # ... other apps
    "ckeditor",
]

Then I configured CKEditor with custom settings:

# CKEditor settings
CKEDITOR_CONFIGS = {
    'default': {
        'toolbar': 'full',
        'height': 300,
        'width': '100%',
    },
    'blog_content': {
        'toolbar': [
            ['Format', 'Bold', 'Italic', 'Underline', 'Strike', 'SpellChecker'],
            ['NumberedList', 'BulletedList', 'Indent', 'Outdent', 'JustifyLeft', 'JustifyCenter', 'JustifyRight', 'JustifyBlock'],
            ['Link', 'Unlink', 'Anchor'],
            ['Image', 'Table', 'HorizontalRule', 'SpecialChar'],
            ['Source', 'Maximize'],
            ['Undo', 'Redo'],
        ],
        'height': 300,
        'width': '100%',
        'removePlugins': 'stylesheetparser',
        'extraPlugins': 'codesnippet',
    },
}

I created a custom configuration called 'blog_content' with a specific toolbar setup and added the 'codesnippet' plugin for code highlighting.

Model Implementation

I designed a flexible blog content model that uses CKEditor's RichTextField:

from django.db import models
from ckeditor.fields import RichTextField

class ModelBase(models.Model):
    created_at = models.DateTimeField(auto_now_add=True, editable=False)
    updated_at = models.DateTimeField(auto_now=True, editable=False)

    class Meta:
        abstract = True

class BlogContent(ModelBase):
    CONTENT_TYPES = (
        ('text', 'Text'),
        ('heading', 'Heading'),
        ('image', 'Image'),
        ('code', 'Code Snippet'),
        ('quote', 'Quote'),
        ('list', 'List'),
    )

    blog = models.ForeignKey('Blogs', related_name='content_blocks', on_delete=models.CASCADE)
    content_type = models.CharField(max_length=20, choices=CONTENT_TYPES)
    order = models.PositiveIntegerField(default=0)
    content = RichTextField(config_name='blog_content', blank=True, null=True)
    plain_content = models.TextField(blank=True, null=True, help_text="Plain text content for code snippets, lists, etc.")
    extra_data = models.JSONField(blank=True, null=True, help_text="Additional data for the content block")

    class Meta:
        ordering = ['order']

The key part is using RichTextField with the config_name='blog_content' parameter, which links to our custom configuration.

Admin Integration

I set up the admin interface to provide a great editing experience:

from django.contrib import admin
from .models import BlogContent, Blogs

class BlogContentInline(admin.StackedInline):
    model = BlogContent
    extra = 1
    fieldsets = (
        (None, {
            'fields': ('content_type', 'order')
        }),
        ('Content', {
            'fields': ('content', 'plain_content'),
        }),
        ('Advanced options', {
            'classes': ('collapse',),
            'fields': ('extra_data',),
        }),
    )

    class Media:
        js = ('admin/js/blog_content_admin.js',)

@admin.register(Blogs)
class BlogsAdmin(admin.ModelAdmin):
    list_display = ('title', 'author', 'subject', 'created_at')
    list_filter = ('author', 'subject', 'created_at')
    search_fields = ('title', 'description', 'tags')
    inlines = [BlogContentInline]
    fieldsets = (
        (None, {
            'fields': ('title', 'subject', 'tags', 'author', 'featured_image')
        }),
        ('Content', {
            'fields': ('description',)
        }),
    )

The BlogContentInline class allows adding multiple content blocks to a blog post, each with its own CKEditor instance.

Custom JavaScript for Enhanced Experience

I added a custom JavaScript file to enhance the admin interface:

// admin/js/blog_content_admin.js
document.addEventListener('DOMContentLoaded', function() {
    // Show/hide fields based on content type
    function toggleFields() {
        const contentTypeSelects = document.querySelectorAll('select[id$="-content_type"]');
        
        contentTypeSelects.forEach(select => {
            select.addEventListener('change', function() {
                const row = this.closest('.form-row').parentNode;
                const contentField = row.querySelector('.field-content');
                const plainContentField = row.querySelector('.field-plain_content');
                
                if (this.value === 'code') {
                    contentField.style.display = 'none';
                    plainContentField.style.display = 'block';
                } else if (this.value === 'text' || this.value === 'heading') {
                    contentField.style.display = 'block';
                    plainContentField.style.display = 'none';
                } else {
                    contentField.style.display = 'block';
                    plainContentField.style.display = 'block';
                }
            });
            
            // Trigger on load
            select.dispatchEvent(new Event('change'));
        });
    }
    
    // Call on page load
    toggleFields();
    
    // Also call when new inline forms are added
    django.jQuery(document).on('formset:added', toggleFields);
});

This script dynamically shows/hides fields based on the content type selected, providing a more intuitive editing experience.

Conclusion

Adding django-ckeditor to my Django admin has significantly improved the content creation experience. The rich text editor makes it easy to create well-formatted blog posts with various content types, including text, images, and code snippets.

The modular approach with different content blocks allows for flexible blog layouts while maintaining a structured data model. This setup is perfect for technical blogs where you need to mix explanatory text with code examples.

By customizing the CKEditor configuration and enhancing the admin interface with custom JavaScript, I've created a powerful yet user-friendly blog editor that makes content creation a breeze.

Author
Apurv Chaudhary

Software Engineer & Technical Writer