WDTUTORIALS
menu
Django - The Easy Way Django - The Easy Way
Samuli Natri 2018.03.16
Samuli Natri is a software developer who enjoys programming games and web applications. He attended Helsinki University Of Technology (Computer Science) and Helsinki University (Social Sciences).

Django - CKEditor Tutorial With CodeSnippet Syntax Highlighting

How to use CKEditor in custom forms (+ admin area) and add CodeSnippet + Youtube plugins.

Installation

Install django-editor package:

pip install django-ckeditor

Add ckeditor to the INSTALLED_APPS list in settings.py:

INSTALLED_APPS = [
    ...
    'ckeditor',
]

Add RichTextField

Add a field to some of your models:

from ckeditor.fields import RichTextField

description = RichTextField(blank=True, null=True)

Run migrations:

python manage.py makemigrations && python manage.py migrate

Now you can use the wysiwyg editor in the Admin area when using the description field.

Serving Static Files In Development And Production

CKEDitor static assets (js, css) are loaded automatically when we are using the development server.

If you have django.contrib.staticfiles listed in the INSTALLED_APPS list, then Django will serve the static files automatically in DEBUG mode.

But in production, you should define STATIC_URL and STATIC_ROOT...

STATIC_URL = '/static/'
STATIC_ROOT = '/home/mysite/static/'

...and run python manage.py collectstatic to collect the resources to the STATIC_ROOT folder.

In Nginx you could add a these lines to the nginx configuration file to serve static and media files:

   location /static/ {
        alias /home/mysite/static/;
    }
   location /media/ {
        alias /home/mysite/media/;
    }

How To Upload Files

Add ckeditor_uploader and CKEDITOR_UPLOAD_PATH to the settings file:

INSTALLED_APPS = [
    ...
    'ckeditor',
    'ckeditor_uploader', # < here
]
CKEDITOR_UPLOAD_PATH = "uploads/" # < here

Include CKEditor urls in the main urls.py file:

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('base.urls')),
    path('ckeditor/', include('ckeditor_uploader.urls')), # < here
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

Make sure to serve user uploaded media files in development by adding this to urlpatterns:

+ static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

And defining these in the settings.py file:

MEDIA_URL = '/media/'
MEDIA_ROOT = 'media/'

For file uploads we have to change the field type to RichTextUploadingField:

# description = RichTextField(blank=True, null=True)
description = RichTextUploadingField(blank=True, null=True)

Run migrations:

python manage.py makemigrations && python manage.py migrate

Go to the admin area and you should be able to upload images with the description field.

Use the safe filter in templates to show all the HTML markup:

{{ post.description | safe }}

Custom Forms

Create forms.py file inside some app folder. I will be using the blog app.

Add these lines in it:

from django.forms import ModelForm
from .models import Post

class PostForm(ModelForm):
    class Meta:
        model = Post
        fields = ['description']

Edit main urls.py file and add these lines:

from blog import views as blog_views # < here

path('ckeditor/', include('ckeditor_uploader.urls')),
path('add/post/', blog_views.add_post, name='add_post'), # < here
path('edit/post/<int:post_id>/', blog_views.edit_post, name='edit_post'), # < here

Edit the blog app views.py file and add these lines:

from django.shortcuts import render
from .forms import PostForm

def add_post(request):
    if request.method == "POST":
        form = PostForm(request.POST)
        if form.is_valid():
            post_item = form.save(commit=False)
            post_item.save()
            return redirect('/')
    else:
        form = PostForm()
    return render(request, 'blog/post_form.html', {'form': form})

Create the form template in /blog/templates/blog/post_form.html:

<form method="POST" action="">
    {% csrf_token %}
    {{ form.media }}
    {{ form }}
    <input type="submit" value="Save">
</form>

Now you can add posts in /add/post/.

Edit main urls.py file and add this line:

from blog import views as blog_views

path('ckeditor/', include('ckeditor_uploader.urls')),
path('add/post/', blog_views.add_post, name='add_post'),
path('edit/post/<int:post_id>/', blog_views.edit_post, name='edit_post'), # < here

Edit the blog app views.py file and add these:

def edit_post(request, post_id=None):
    item = get_object_or_404(Post, id=post_id)
    form = PostForm(request.POST or None, instance=item)
    if form.is_valid():
        form.save()
        return redirect('/')
    return render(request, 'blog/post_form.html', {'form': form})

Now you can edit posts in /edit/post/[id]/.

Custom Configuration

You can customize the toolbar with CKEDITOR_CONFIGS dictionary.

Put this in the settings.py file:

CKEDITOR_CONFIGS = {
    'default': {
        'toolbar': 'Custom',
        'toolbar_Custom': [
            ['Bold', 'Link', 'Unlink', 'Image'],
        ],
    }
}

Now by default you have just these 4 options in the toolbar.

Check the https://docs.ckeditor.com/ckeditor4/latest/api/CKEDITOR_config.html#cfg-height for more configuration options.

Here I change the height to 500 px:

CKEDITOR_CONFIGS = {
    'default': {
        'toolbar': 'Custom',
        'height': 500, # < here
        'toolbar_Custom': [
            ['Bold', 'Link', 'Unlink', 'Image'],
        ],
    }
}

You can define multiple configurations like this:

CKEDITOR_CONFIGS = {
    'default': {
        'toolbar': 'Custom',
        'height': 500,
        'toolbar_Custom': [
            ['Bold', 'Link', 'Unlink', 'Image'],
        ],
    },
    'special': {
        'toolbar': 'Special',
        'toolbar_Special': [
            ['Bold'],
        ],
    }
}

And then define the configuration in models.py:

description = RichTextUploadingField(blank=True, null=True)
description2 = RichTextUploadingField(blank=True, null=True, config_name='special') # < here

Run migrations:

python manage.py makemigrations && python manage.py migrate

Now the first description field gets the default settings and description2 field gets our special configuration.

You can find the default Full toolbar in the widgets.py file:

vim venv/lib/python3.6/site-packages/ckeditor/widgets.py

Here we use the default Full toolbar options for our Custom toolbar:

CKEDITOR_CONFIGS = {
    'default': {
        'toolbar': 'Custom',
        'height': 500,
        'toolbar_Custom': [
            ['Styles', 'Format', 'Bold', 'Italic', 'Underline', 'Strike', 'SpellChecker', 'Undo', 'Redo'],
            ['Link', 'Unlink', 'Anchor'],
            ['Image', 'Flash', 'Table', 'HorizontalRule'],
            ['TextColor', 'BGColor'],
            ['Smiley', 'SpecialChar'], ['Source'],
        ],
    },
    'special': {
        'toolbar': 'Special',
        'toolbar_Special': [
            ['Bold'],
        ],
    }
}

Extra Plugins (CodeSnippet)

The package ships with some extra plugins that are not enabled by default.

Here is an example how to enable CodeSnippet syntax highlighter:

    'special': {
        'toolbar': 'Special',
        'toolbar_Special': [
            ['Bold'], ['CodeSnippet'], # < here
        ],
        'extraPlugins': 'codesnippet', # < here
    }

Now you should have a CodeSnippet icon in the Special toolbar.

You also need some syntax highlighter.  My personal favourite https://highlightjs.org/.

Add this to your html head section:

<link rel="stylesheet"
      href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/default.min.css">
<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js"></script>
<script src="/static/base/js/main.js"></script>
<script>hljs.initHighlightingOnLoad();</script>

Now you should see the syntax highlighting working when you print out the field in templates like this:

{{ post.description2 | safe }}

Custom Plugins (Youtube)

Here is how you can add more plugins.

Use external_plugin_resources in models.py:

description2 = RichTextUploadingField(blank=True, null=True,
                                      config_name='special',
                                      external_plugin_resources=[(
                                          'youtube',
                                          '/static/base/vendor/ckeditor_plugins/youtube/youtube/',
                                          'plugin.js',
                                          )],
                                      )

In this example we add the https://ckeditor.com/cke4/addon/youtube.

Download the plugin and put it in some app's static folder. In here I use the base app. I put external libraries in the vendor folder:

cd base/static/base/vendor
mkdir -p ckeditor_plugins/youtube
cd ckeditor_plugins/youtube
wget https://download.ckeditor.com/youtube/releases/youtube_2.1.10.zip --no-check-certificate
unzip youtube_2.1.10.zip
rm youtube_2.1.10.zip

Enable the plugin in the settings.py file:

    'special': {
        'toolbar': 'Special',
        'toolbar_Special': [
            ['Bold'], ['CodeSnippet', 'Youtube'],
        ],
        'extraPlugins': ','.join(['codesnippet', 'youtube']),
    }

And now you can also embed youtube videos.