Quickstart
mysite/settings.py
Add these lines to the project settings file:
INSTALLED_APPS = [
# START
'blog.apps.BlogConfig',
'ckeditor',
'ckeditor_uploader',
# END
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
# START
CKEDITOR_UPLOAD_PATH = 'uploads/'
MEDIA_URL = '/media/'
MEDIA_ROOT = 'media/'
# END
blog/models.py
Add RichTextUploadingField
to a model:
from ckeditor_uploader.fields import RichTextUploadingField
class Post(models.Model):
body = RichTextUploadingField(blank=True)
mysite/urls.py
Edit the project URL configuration file and include ckeditor URLs. Serve uploaded media files using the static
function:
urlpatterns = [
# HERE
path('ckeditor/', include(
'ckeditor_uploader.urls')),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
templates/blog/post_detail.html
Use the safe
filter in templates:
<p>{{ post.body|safe }}</p>
templates/blog/post_form.html
Use the {{ form.media }}
tag with forms:
<form method="post">
{% csrf_token %}
{{ form.media }}
{{ form.as_p }}
<input type="submit" value="Save">
</form>
Full tutorial
CKEditor is a rich text WYSIWYG editor. This tutorial shows how to add a RichTextUploading field to a model that enables all basic CKEditor features plus allows users to upload images and insert code snippets.
Setup
Run these commands to setup a project:
mkdir mysite && cd mysite
python3 -m venv venv
source venv/bin/activate
pip install django django-ckeditor
django-admin startproject mysite .
python manage.py startapp blog
mysite/settings.py
Add these lines to the project settings.py
file:
INSTALLED_APPS = [
# START
'blog.apps.BlogConfig',
'ckeditor',
'ckeditor_uploader',
# END
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
CKEDITOR_UPLOAD_PATH = 'uploads/'
MEDIA_URL = '/media/'
MEDIA_ROOT = 'media/'
TEMPLATES = [
{
# ...
# HERE
'DIRS': [os.path.join(BASE_DIR, 'templates')],
# ...
},
]
- Add
ckeditor
to theINSTALLED_APPS
list. - Add
ckeditor_uploader
to theINSTALLED_APPS
list if you want to upload images. - We enable the
blog
app by adding its configuration class to theINSTALLED_APPS
list. CKEDITOR_UPLOAD_PATH
specifies the directory where the uploaded files are stored inside theMEDIA_ROOT
directory (media/uploads
).- The
MEDIA_URL
specifies the URL that we use to serve the uploaded files to the client (mysite.com/media/uploads/2020/06/11/04.jpg
). MEDIA_ROOT
is the physical directory where the files are stored. In this case themedia
directory in the project root.'DIRS': [os.path.join(BASE_DIR, 'templates')]
makes Django look for template files in the project roottemplates
directory.
blog/models.py
Add a class called Post
to the blog app models.py
file:
from django.db import models
from ckeditor_uploader.fields import RichTextUploadingField
class Post(models.Model):
body = RichTextUploadingField(blank=True)
- The
RichTextUploadingField
field works like the standardTextField
but also adds the CKEditor rich text capabilities to it. UseRichTextField
if you don't need to upload files.
Run migrations:
python manage.py makemigrations && \
python manage.py migrate
python manage.py createsuperuser
This makes the necessary changes to the database and creates a superuser.
mysite/urls.py
Edit the project urls.py
file and add these lines to it:
from django.contrib import admin
# START
from django.urls import include, path
from django.conf.urls.static import static
from django.conf import settings
# END
urlpatterns = [
# START
path('blog/', include('blog.urls')),
path('ckeditor/', include(
'ckeditor_uploader.urls')),
# END
path('admin/', admin.site.urls)
# HERE
] + static(settings.MEDIA_URL,
document_root=settings.MEDIA_ROOT)
- Blog URLs are stored in the blog app
urls.py
file. - Include the
ckeditor_uploader
URLs to add views that can upload and browse files. By default these views require the Staff status permission. - The
static
function allows us to serve uploaded media files during development.
blog/urls.py
Create a file called urls.py
in the blog app directory:
from django.urls import path
from .views import PostDetailView
app_name = 'blog'
urlpatterns = [
path('<int:pk>/',
PostDetailView.as_view(),
name='detail'),
]
This routes mysite.com/blog/<id>/
to the PostDetailView
.
blog/views.py
Edit the blog app views.py
file and add these lines to it:
from django.views.generic import DetailView
from .models import Post
class PostDetailView(DetailView):
model = Post
template_name = 'blog/post_detail.html'
With the PostDetailView
class we display data about one post object using the post_detail.html
template file.
templates/blog/post_detail.html
Create a directory called templates
in the project root. Add a directory called blog
in it. Add a file called post_detail.html
inside the blog
subdirectory.
<p>{{ post.body|safe }}</p>
- The
safe
filter allows us to render the necessary HTML tags that Django escapes by default. Otherwise<strong>Hello</strong>
actually looks like<strong>Hello</strong>
on the page, not "Hello".
Add Post
Add these lines to the blog app admin.py
file:
from django.contrib import admin
from .models import Post
admin.site.register(Post)
This allows us to manage Post
objects in the admin.
Visit admin and add a blog post with an image:
- Click the Upload tab.
- Choose a file.
- Cick the Send it to the server button.
The interface allows you to change some image properties. The width and height settings just sets the element inline style: style="height:225px; width:300px"
. Keep this in mind if you upload large images:
Visit the blog detail page in mysite.com/blog/1/
and you should see the formatted output:
Custom Forms
CKEditor also works outside the admin. Add the following path to the blog app urls.py
file:
from django.urls import path
# HERE
from .views import PostCreateView, PostDetailView
app_name = 'blog'
urlpatterns = [
path('<int:pk>/',
PostDetailView.as_view(),
name='detail'),
# HERE
path('create/',
PostCreateView.as_view(),
name='create'),
]
Edit the blog app views.py
file and a class called PostCreateView
to it:
from django.views.generic import DetailView
# START
from django.views.generic.edit import CreateView
from django.urls import reverse
# END
from .models import Post
class PostDetailView(DetailView):
model = Post
template_name = 'blog/post_detail.html'
# HERE
class PostCreateView(CreateView):
model = Post
fields = ['body']
def get_success_url(self):
return reverse('blog:detail',
args=[self.object.pk])
Create a file called post_form.html
in the templates/blog/
directory:
<form method="post">
{% csrf_token %}
{{ form.media }}
{{ form.as_p }}
<input type="submit" value="Save">
</form>
- Use the
{{ form.media }}
tag to include required media assets.
Visit mysite.com/blog/create/
to create an item. Make sure you are logged-in.
Toolbar Customization
You can customize the toolbar by configuring the CKEDITOR_CONFIGS
setting. Put this in the project settings.py
file:
CKEDITOR_CONFIGS = {
'default':
{'toolbar': 'Custom',
'toolbar_Custom': [
['Bold', 'Link', 'Unlink', 'Image'],
],
}}
Now the toolbar has only these options:
You can define multiple configurations like this:
CKEDITOR_CONFIGS = {
'default':
{'toolbar': 'Custom',
'toolbar_Custom': [
['Bold', 'Link', 'Unlink', 'Image'],
],
},
'special':
{'toolbar': 'Special', 'height': 500,
'toolbar_Special':
[
['Bold'],
],
}
}
The special
editor can only make texts bold and its default height is 500
px. You can find the default Full toolbar configuration in the CKEditor package widgets.py
file:
site-packages/ckeditor/widgets.py
Edit the blog app models.py
file and specify the configuration using the config_name
argument:
class Post(models.Model):
body = RichTextUploadingField(blank=True,
config_name='special')
CodeSnippet Plugin
The ckeditor-package
includes bunch of 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:
CKEDITOR_CONFIGS = {
'default':
{'toolbar': 'Custom',
'toolbar_Custom': [
['Bold', 'Link', 'Unlink', 'Image'],
],
},
'special':
{'toolbar': 'Special', 'height': 500,
'toolbar_Special':
[
['Bold'],
['CodeSnippet'], # here
], 'extraPlugins': 'codesnippet', # here
}
}
Edit a post and add some code to it using the widget:
def make_pizza():
add_ingredients(['Tomatoes', 'Mozzarella', 'Basil'])
cook()
I'm using the highlight.js library for syntax highlighting. Edit the post_detail.html
template file and add these lines to it:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!-- highlight.js-->
<link rel="stylesheet"
href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/10.0.3/styles/default.min.css">
<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/10.0.3/highlight.min.js"></script>
<script>hljs.initHighlightingOnLoad();</script>
<!-- highlight.js -->
</head>
<body>
<p>{{ post.body|safe }}</p>
</body>
</html>
Refresh the blog detail page and you should see the code snippet highlighted: