There are a lot of WYSIWYG editors available on the internet, some are paid and some are free. After exploring and experimenting with various editors I found tinymce one of the best WYSIWYG editors. So in this tutorial, I will show how you can install and integrate this editor to your Django admin from scratch and without using any pip package. It makes this simpler.

Link to the repository: https://github.com/raturitechmedia/django-tinymce-integration

Step 1: Blog Model

For this tutorial, I am assuming that you have a model and a model field corresponding to which you want to add this editor.

For this tutorial let's take this simple model.

# blog/models.py
class Blog(models.Model):
    title = models.CharField(max_length=255, unique=True)
    body = models.TextField()

    def __str__(self):
        return self.title

Step 2: Overriding Blog default form

Create a file under the app blog - blog/forms.py and add a id to the field body.

# blog/forms.py

from django import forms
from .models import *

class BlogAdminForm(forms.ModelForm):
    body = forms.CharField(widget=forms.Textarea(attrs={'id': "richtext_field"}))

    class Meta:
        model = Blog
        fields = "__all__"

Step 3: Adding the above form to django admin

Now set the form attribute of your admin class to the form created above.

from django.contrib import admin
from .models import *
from .forms import *

class BlogAdmin(admin.ModelAdmin):
    form = BlogAdminForm

admin.site.register(Blog, BlogAdmin)

Step 4: Override django admin base.html

Now we need to override django's admin existing base.html template to add tinymce configuration. This step is very important because this will convert django's form field to a WYSIWYG editor.

Make sure you have added path to templates directory inside your settings.py, in this case it looks like this.

# settings.py

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

Now, create a file admin/base.html inside your templates directory.

{% extends "admin/base.html" %}
{% load static %}

{% block footer %}
    {{ block.super }}
    <script src="https://cdn.tiny.cloud/1/<API_KEY>/tinymce/5/tinymce.min.js"
            referrerpolicy="origin"></script>
    <script>

        tinymce.init({
            selector: '#richtext_field',
            plugins: 'print preview powerpaste casechange importcss tinydrive searchreplace autolink autosave save directionality advcode visualblocks visualchars fullscreen image link media mediaembed template codesample table charmap hr pagebreak nonbreaking anchor toc insertdatetime advlist lists checklist wordcount tinymcespellchecker a11ychecker imagetools textpattern noneditable help formatpainter permanentpen pageembed charmap tinycomments mentions quickbars linkchecker emoticons advtable blockquote',
            menubar: 'file edit view insert format tools table tc help',
            toolbar: 'undo redo | bold italic underline strikethrough | fontselect fontsizeselect formatselect | alignleft aligncenter alignright alignjustify | outdent indent |  numlist bullist checklist | forecolor backcolor casechange permanentpen formatpainter removeformat | pagebreak | charmap emoticons | fullscreen  preview save print | insertfile image media pageembed template link anchor codesample | a11ycheck ltr rtl | showcomments addcomment',
            autosave_ask_before_unload: true,
            autosave_interval: '30s',
            autosave_prefix: '{path}{query}-{id}-',
            autosave_restore_when_empty: false,
            autosave_retention: '2m',
            image_advtab: true,
            link_list: [
                {title: 'My page 1', value: 'https://www.tiny.cloud'},
                {title: 'My page 2', value: 'http://www.moxiecode.com'}
            ],
            image_list: [
                {title: 'My page 1', value: 'https://www.tiny.cloud'},
                {title: 'My page 2', value: 'http://www.moxiecode.com'}
            ],
            image_class_list: [
                {title: 'None', value: ''},
                {title: 'Some class', value: 'class-name'}
            ],
            importcss_append: true,
            templates: [
                {
                    title: 'New Table',
                    description: 'creates a new table',
                    content: '<div class="mceTmpl"><table width="98%%"  border="0" cellspacing="0" cellpadding="0"><tr><th scope="col"> </th><th scope="col"> </th></tr><tr><td> </td><td> </td></tr></table></div>'
                },
                {title: 'Starting my story', description: 'A cure for writers block', content: 'Once upon a time...'},
                {
                    title: 'New list with dates',
                    description: 'New List with dates',
                    content: '<div class="mceTmpl"><span class="cdate">cdate</span><br /><span class="mdate">mdate</span><h2>My List</h2><ul><li></li><li></li></ul></div>'
                }
            ],
            template_cdate_format: '[Date Created (CDATE): %m/%d/%Y : %H:%M:%S]',
            template_mdate_format: '[Date Modified (MDATE): %m/%d/%Y : %H:%M:%S]',
            height: 600,
            image_caption: true,
            quickbars_selection_toolbar: 'bold italic | quicklink h2 h3 blockquote quickimage quicktable',
            noneditable_noneditable_class: 'mceNonEditable',
            toolbar_mode: 'sliding',
            spellchecker_ignore_list: ['Ephox', 'Moxiecode'],
            tinycomments_mode: 'embedded',
            content_style: '.mymention{ color: gray; }',
            contextmenu: 'link image in',
            a11y_advanced_options: true,
            mentions_selector: '.mymention',
            mentions_item_type: 'profile'
        });
    </script>
{% endblock %}

Step 5: Getting API Key

Create an account here for an API key: https://www.tiny.cloud/auth/signup

Now Copy the API from your dashboard

Step 6: Replace the API KEY

Replace the API KEY that you just copied from the tinymce dashboard inside the script tag inside the template admin/base.html

After replacing your script tag should look something like this

<script src="https://cdn.tiny.cloud/1/pzasdsadigrztlljshvt5l05zlhasdsadhhw4pqthj9btdbq6ashdm/tinymce/5/tinymce.min.js"
            referrerpolicy="origin"></script>

Step 6: Verifying WYSIWYG editor

Now run your server and login to admin portal, there open your blog model and then click on add blog.

Now you can see that your body form field have been turned into WYSIWYG editor.

 

Step 7: Adding Syntax highlighting

Now in your blog detail view template where you have rendered your blog model body using {{object.body | safe}} , add the prism css and js files to enable syntax highlighting.

Download the css and js from the prism website: https://prismjs.com/download.html

 

That's all, I hope it would help you in writing blog posts or content more easily than before.