Integrating CKEditor in Django Admin and Rendering HTML in a template - Django Blog #4

Hello Internet People, today we integrate CKEditor in Django admin. CKEditor is a rich text editor. So, activate your virtual environment.

Install ckeditor django package

Just type following command to install,

pip3 install django-ckeditor

Now mention ckeditor in INSTALLED_APP in settings.py like following,

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'blog',
    'ckeditor', #this
]

Settings for media upload

open settings.py and add following at last

MEDIA_URL = "/media/"
MEDIA_ROOT = BASE_DIR / "media/"

Add ckeditor uploader in the INSTALLED_APP

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'blog',
    'ckeditor', 
    'ckeditor_uploader', #this
]

Now set the upload path for ckeditor in settings.py. add following code at last,

#ckeditor upload path
CKEDITOR_UPLOAD_PATH="uploads/"

This uploads directory will automatically create inside media directory of the root directory

Setup url

Open urls.py of awwblog and make look like following,

from django.contrib import admin
from django.urls import path, include

from django.conf import settings
from django.conf.urls.static import static

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('blog.urls', namespace='blog')),
    path('ckeditor/',include('ckeditor_uploader.urls')),
]+static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

Change in model

We must change in model to get rich text editor in admin. Open models.py of blog application and make change like following,

make change in Post model like following,

from ckeditor_uploader.fields import RichTextUploadingField #import this

class Post(models.Model):
    ...
    # body = models.TextField() # remove this 
    body=RichTextUploadingField() # add this

Now run migrations,

python3 manage.py makemigrations

and migrate,

python3 manage.py migrate

run development server using python3 manage.py runserver and open http://127.0.0.1:8000/admin and click on add post or open any other posts, you can notice, rich text editor now here in body field,

ckeditor in django adming

ckeditor

Now you can add headings, bullets, bold text, images, etc.

Let’s add post with headings and image.

adding post with ckeditor

Now open blog post which you added with ckeditor, here you can notice HTML tags are not rendering.

html not rendering

Filter and Tag in HTML template

To fix this we add safe filter in post_detail.html. So open post_detail.html and make following change,

 <div class="article-body">
     {{post.body|safe}} <!-- remove linebreaks -->
 </div>

Now again go to that post and refresh the page,

html rendered

html rendered

But there is one problem too in home page,

Open post_list.html and make following change,

...
 <p class="card-text mb-auto py-2">{{ post.body|safe|striptags|truncatechars:100 }}</p>
                              
 <div>
   <a href="{{ post.get_absolute_url }}" class="btn btn-primary btn-sm">Read more</a>
 </div>
...

Now you can see we get desire output,

not rendering html in post excerpt

Customizing CKEditor

As you can notice in the above CKEditor, there are enough functions to edit the content but we can add the function to it like source code highlighter. So let’s add more functionality,

open settings.py and add following code,

before that open this link and configure your CKEditor, add or remove whatever you want.

now open settings.py and add following code at last,

CKEDITOR_CONFIGS = {
    'default': {
     
        # 'skin': 'moono',
        # # 'skin': 'office2013',
        # 'toolbar_Basic': [
        #     ['Source', '-', 'Bold', 'Italic']
        # ],
        'toolbar_Custom': [
            {'name': 'document', 'items': ['Source', '-', 'Save', 'NewPage', 'Preview', 'Print', '-', 'Templates']},
            {'name': 'clipboard', 'items': ['Cut', 'Copy', 'Paste', 'PasteText', 'PasteFromWord', '-', 'Undo', 'Redo']},
            {'name': 'editing', 'items': ['Find', 'Replace', '-', 'SelectAll']},
            {'name': 'forms',
             'items': ['Form', 'Checkbox', 'Radio', 'TextField', 'Textarea', 'Select', 'Button', 'ImageButton',
                       'HiddenField']},
            '/',
            {'name': 'basicstyles',
             'items': ['Bold', 'Italic', 'Underline', 'Strike', 'Subscript', 'Superscript', '-', 'RemoveFormat']},
            {'name': 'paragraph',
             'items': ['NumberedList', 'BulletedList', '-', 'Outdent', 'Indent', '-', 'Blockquote', 'CreateDiv', '-',
                       'JustifyLeft', 'JustifyCenter', 'JustifyRight', 'JustifyBlock', '-', 'BidiLtr', 'BidiRtl',
                       'Language']},
            {'name': 'links', 'items': ['Link', 'Unlink', 'Anchor']},
            {'name': 'insert',
             'items': ['Image', 'Youtube','Flash', 'Table', 'HorizontalRule', 'Smiley', 'SpecialChar', 'PageBreak', 'Iframe']},
            '/',
            {'name': 'styles', 'items': ['Styles', 'Format', 'Font', 'FontSize']},
            {'name': 'colors', 'items': ['TextColor', 'BGColor']},
            {'name': 'tools', 'items': ['Maximize', 'ShowBlocks']},
            {'name': 'about', 'items': ['CodeSnippet']},
            {'name': 'about', 'items': ['About']},
            '/',  # put this to force next toolbar on new line
            {'name': 'yourcustomtools', 'items': [
                # put the name of your editor.ui.addButton here
                'Preview',
                'Maximize',

            ]},
        ],
        'toolbar': 'Custom',  # put selected toolbar config here
        'toolbarGroups': [{ 'name': 'document', 'groups': [ 'mode', 'document', 'doctools' ] }],
        'height': 400,
        # 'width': '100%',
        'filebrowserWindowHeight': 725,
        'filebrowserWindowWidth': 940,
        'toolbarCanCollapse': True,
        'mathJaxLib': '//cdn.mathjax.org/mathjax/2.2-latest/MathJax.js?config=TeX-AMS_HTML',
        'tabSpaces': 4,
        'extraPlugins': ','.join([
            'uploadimage', # the upload image feature
            # your extra plugins here
            'div',
            'autolink',
            'autoembed',
            'embedsemantic',
            'autogrow',
            'devtools',
            'widget',
            'lineutils',
            'clipboard',
            'dialog',
            'dialogui',
            'elementspath',
            'codesnippet',
        ]),
    }
}

Now open admin and you see following change,

customizing ckeditor

custom ckeditor

You can now add more functions too in your content, one of them is code highlighter,

let’s see the example,

code highlighter

code highlighter

Now this working fine but let’s see in the post,

code highlighter in ckeditor django blog tutorial

blog post

This not looks good right. So, let’s fix it.

We solve this with using highlight.js. Open base.html and add following CDN url and scripts in head tag,

 <!-- highlight js -->
    <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.1/styles/monokai-sublime.min.css">
    <script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.1/highlight.min.js"></script>
    <script>hljs.initHighlightingOnLoad();</script>
    

Now refresh the blog post page and you notice the change like following,

It’s looks good right. You can also add other code highlighter js library too according to your need.

add this css to make image responsive inside our post.

article img{
    max-width: 100% !important;
    height: auto !important;
    border-radius: .25rem!important;
}

Note: You can also add bootstrap class using javascript. But I choose this way.

We learned many things today. Hope you are enjoying this series.

GitHub Link: https://github.com/SoniArpit/awwblog

Previous: Displaying List of Posts and Single Post on a Web page and Pagination – Django Blog #3

Next: Adding Featured Image in Blog Posts – Django Blog #5