在Django中安装、配置、使用CKEditor5,并将CKEditor5录入的文章展现出来,实现一个简单博客网站的功能

在Django中可以使用CKEditor4和CKEditor5两个版本,分别对应软件包django-ckeditor和django-ckeditor-5。原来使用的是CKEditor4,python manager.py makemigrations时总是提示CKEditor4有安全风险,建议升级到CKEditor5。故卸载了CKEditor4,安装配置CKEditor5,具体步骤如下:

  1. 安装CKEditor5(Debian系统):

    sudo pip3 install django-ckeditor-5

  2. 将"django_ckeditor_5"添加到settings.py的INSTALLED_APPS中:

    INSTALLED_APPS = [
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'django_ckeditor_5',
    ......
    ]

  3. 在settings.py中配置CKEditor5(官网标准设置):

    STATIC_URL = '/static/'
    MEDIA_URL = '/media/'
    MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

    customColorPalette = [
    {
    'color': 'hsl(4, 90%, 58%)',
    'label': 'Red'
    },
    {
    'color': 'hsl(340, 82%, 52%)',
    'label': 'Pink'
    },
    {
    'color': 'hsl(291, 64%, 42%)',
    'label': 'Purple'
    },
    {
    'color': 'hsl(262, 52%, 47%)',
    'label': 'Deep Purple'
    },
    {
    'color': 'hsl(231, 48%, 48%)',
    'label': 'Indigo'
    },
    {
    'color': 'hsl(207, 90%, 54%)',
    'label': 'Blue'
    },
    ]

    CKEDITOR_5_CUSTOM_CSS = 'path_to.css' # optional
    CKEDITOR_5_FILE_STORAGE = "path_to_storage.CustomStorage" # optional
    CKEDITOR_5_CONFIGS = {
    'default': {
    'toolbar': ['heading', '|', 'bold', 'italic', 'link',
    'bulletedList', 'numberedList', 'blockQuote', 'imageUpload', ],

     },
     'extends': {
         'blockToolbar': [
             'paragraph', 'heading1', 'heading2', 'heading3',
             '|',
             'bulletedList', 'numberedList',
             '|',
             'blockQuote',
         ],
         'toolbar': ['heading', '|', 'outdent', 'indent', '|', 'bold', 'italic', 'link', 'underline', 'strikethrough',
         'code','subscript', 'superscript', 'highlight', '|', 'codeBlock', 'sourceEditing', 'insertImage',
                     'bulletedList', 'numberedList', 'todoList', '|',  'blockQuote', 'imageUpload', '|',
                     'fontSize', 'fontFamily', 'fontColor', 'fontBackgroundColor', 'mediaEmbed', 'removeFormat',
                     'insertTable',],
         'image': {
             'toolbar': ['imageTextAlternative', '|', 'imageStyle:alignLeft',
                         'imageStyle:alignRight', 'imageStyle:alignCenter', 'imageStyle:side',  '|'],
             'styles': [
                 'full',
                 'side',
                 'alignLeft',
                 'alignRight',
                 'alignCenter',
             ]
    
         },
         'table': {
             'contentToolbar': [ 'tableColumn', 'tableRow', 'mergeTableCells',
             'tableProperties', 'tableCellProperties' ],
             'tableProperties': {
                 'borderColors': customColorPalette,
                 'backgroundColors': customColorPalette
             },
             'tableCellProperties': {
                 'borderColors': customColorPalette,
                 'backgroundColors': customColorPalette
             }
         },
         'heading' : {
             'options': [
                 { 'model': 'paragraph', 'title': 'Paragraph', 'class': 'ck-heading_paragraph' },
                 { 'model': 'heading1', 'view': 'h1', 'title': 'Heading 1', 'class': 'ck-heading_heading1' },
                 { 'model': 'heading2', 'view': 'h2', 'title': 'Heading 2', 'class': 'ck-heading_heading2' },
                 { 'model': 'heading3', 'view': 'h3', 'title': 'Heading 3', 'class': 'ck-heading_heading3' }
             ]
         }
     },
     'list': {
         'properties': {
             'styles': 'true',
             'startIndex': 'true',
             'reversed': 'true',
         }
     }
    

    }

其中定义了三种配置,分别为"default","extends"和"list",下面主要使用"extends"。

  1. 为了使用中文字体,需要修改extends配置,增加fontFamily设置,将中文字体放在英文字体的前面。

    'fontFamily': {
    'options': ['微软雅黑', '宋体', '黑体', '仿宋', '楷体', '隶书', '幼圆', 'Arial', 'Times New Roman', 'Verdana', 'Helvetica', 'Georgia', 'Courier New', 'Impact', 'Comic Sans MS', 'Trebuchet MS'],
    'supportAllValues': 'true',
    },

效果如下:

  1. 为了使用方便,需要设置字体大小,根据word的使用习惯,按字号来设置字体,修改extends配置,增加fontSize设置。

(1) 如果需要下拉列表的字体大小和设置字体大小一样,可以如下设置:

    'options': [
    { 'model':'56px', 'title': "初号"},
    { 'model':'48px', 'title': "小初"},
    { 'model':'34.7px', 'title': "一号"},
    { 'model':'32px', 'title': "小一"},
    { 'model':'29.3px', 'title': "二号"},
    { 'model':'24px', 'title': "小二"},
    { 'model':'21.3px', 'title': "三号"},
    { 'model':'20px', 'title': "小三"},
    { 'model':'18.7px', 'title': "四号"},
    { 'model':'16px', 'title': "小四"},
    { 'model':'14px', 'title': "五号"},
    { 'model':'12px', 'title': "小五"},
    { 'model':'10px', 'title': "六号"},
    { 'model':'8.7px', 'title': "小六"},
    { 'model':'7.3px', 'title': "七号"},
    { 'model':'6.7px', 'title': "八号"},
    ],
    'supportAllValues': 'true',
   },

效果如下:

(2) 如果不需要下拉列表的字体大小和实际字体大小一样,可以增加显示格式设置,将下拉列表字体大小都统一为14px:

  'fontSize': {
    'options': [
    { 'model':'56px', 'title': "初号", 'view': {'styles': { "font-size": '14px' }}},
    { 'model':'48px', 'title': "小初", 'view': {'styles': { "font-size": '14px' }}},
    { 'model':'34.7px', 'title': "一号", 'view': {'styles': { "font-size": '14px' }}},
    { 'model':'32px', 'title': "小一", 'view': {'styles': { "font-size": '14px' }}},
    { 'model':'29.3px', 'title': "二号", 'view': {'styles': { "font-size": '14px' }}},
    { 'model':'24px', 'title': "小二", 'view': {'styles': { "font-size": '14px' }}},
    { 'model':'21.3px', 'title': "三号", 'view': {'styles': { "font-size": '14px' }}},
    { 'model':'20px', 'title': "小三", 'view': {'styles': { "font-size": '14px' }}},
    { 'model':'18.7px', 'title': "四号", 'view': {'styles': { "font-size": '14px' }}},
    { 'model':'16px', 'title': "小四", 'view': {'styles': { "font-size": '14px' }}},
    { 'model':'14px', 'title': "五号", 'view': {'styles': { "font-size": '14px' }}},
    { 'model':'12px', 'title': "小五", 'view': {'styles': { "font-size": '14px' }}},
    { 'model':'10px', 'title': "六号", 'view': {'styles': { "font-size": '14px' }}},
    { 'model':'8.7px', 'title': "小六", 'view': {'styles': { "font-size": '14px' }}},
    { 'model':'7.3px', 'title': "七号", 'view': {'styles': { "font-size": '14px' }}},
    { 'model':'6.7px', 'title': "八号", 'view': {'styles': { "font-size": '14px' }}},
    ],
    'supportAllValues': 'true',
   },

效果如下:

我个人使用了第二种,另外加上一些常规设置,settings.py中CKEditor5的全部设置如下:

STATIC_ROOT = os.path.join(BASE_DIR,"static/")
MEDIA_URL = "media/"
MEDIA_ROOT = os.path.join(BASE_DIR,"media/")

CKEDITOR_5_CONFIGS = {
'default': {
  'toolbar': ['heading', '|', 'bold', 'italic', 'link',
           'bulletedList', 'numberedList', 'blockQuote', 'imageUpload', ],
},
'extends': {
  'blockToolbar': [
     'paragraph', 'heading1', 'heading2', 'heading3',
     '|',
     'bulletedList', 'numberedList',
     '|',
     'blockQuote',
  ],
  'toolbar': ['heading', '|', 'outdent', 'indent', '|', 'bold', 'italic', 'link', 'underline', 'strikethrough',
  'code','subscript', 'superscript', 'highlight', '|', 'codeBlock', 'sourceEditing', 'insertImage',
           'bulletedList', 'numberedList', 'todoList', '|',  'blockQuote', 'imageUpload', '|',
           'fontSize', 'fontFamily', 'fontColor', 'fontBackgroundColor', 'mediaEmbed', 'removeFormat',
           'insertTable',],
  'image': {
     'toolbar': ['imageTextAlternative', '|', 'imageStyle:alignLeft',
              'imageStyle:alignRight', 'imageStyle:alignCenter', 'imageStyle:side',  '|'],
     'styles': [
        'full',
        'side',
        'alignLeft',
        'alignRight',
        'alignCenter',
     ]

  },
  'table': {
     'contentToolbar': [ 'tableColumn', 'tableRow', 'mergeTableCells',
     'tableProperties', 'tableCellProperties' ],
     'tableProperties': {
        'borderColors': customColorPalette,
        'backgroundColors': customColorPalette
     },
     'tableCellProperties': {
        'borderColors': customColorPalette,
        'backgroundColors': customColorPalette
     }
  },
  'heading' : {
     'options': [
        { 'model': 'paragraph', 'title': 'Paragraph', 'class': 'ck-heading_paragraph' },
        { 'model': 'heading1', 'view': 'h1', 'title': 'Heading 1', 'class': 'ck-heading_heading1' },
        { 'model': 'heading2', 'view': 'h2', 'title': 'Heading 2', 'class': 'ck-heading_heading2' },
        { 'model': 'heading3', 'view': 'h3', 'title': 'Heading 3', 'class': 'ck-heading_heading3' }
     ]
  },

  'fontFamily': {
        'options': ['微软雅黑', '宋体', '黑体', '仿宋', '楷体', '隶书', '幼圆', 'Arial', 'Times New Roman', 'Verdana', 'Helvetica', 'Georgia', 'Courier New', 'Impact', 'Comic Sans MS', 'Trebuchet MS'],
        'supportAllValues': 'true',
    },
  'fontSize': {
    'options': [
    { 'model':'56px', 'title': "初号", 'view': {'styles': { "font-size": '14px' }}},
    { 'model':'48px', 'title': "小初", 'view': {'styles': { "font-size": '14px' }}},
    { 'model':'34.7px', 'title': "一号", 'view': {'styles': { "font-size": '14px' }}},
    { 'model':'32px', 'title': "小一", 'view': {'styles': { "font-size": '14px' }}},
    { 'model':'29.3px', 'title': "二号", 'view': {'styles': { "font-size": '14px' }}},
    { 'model':'24px', 'title': "小二", 'view': {'styles': { "font-size": '14px' }}},
    { 'model':'21.3px', 'title': "三号", 'view': {'styles': { "font-size": '14px' }}},
    { 'model':'20px', 'title': "小三", 'view': {'styles': { "font-size": '14px' }}},
    { 'model':'18.7px', 'title': "四号", 'view': {'styles': { "font-size": '14px' }}},
    { 'model':'16px', 'title': "小四", 'view': {'styles': { "font-size": '14px' }}},
    { 'model':'14px', 'title': "五号", 'view': {'styles': { "font-size": '14px' }}},
    { 'model':'12px', 'title': "小五", 'view': {'styles': { "font-size": '14px' }}},
    { 'model':'10px', 'title': "六号", 'view': {'styles': { "font-size": '14px' }}},
    { 'model':'8.7px', 'title': "小六", 'view': {'styles': { "font-size": '14px' }}},
    { 'model':'7.3px', 'title': "七号", 'view': {'styles': { "font-size": '14px' }}},
    { 'model':'6.7px', 'title': "八号", 'view': {'styles': { "font-size": '14px' }}},
    ],
    'supportAllValues': 'true',
   },
   'height': '800px',
},
'list': {
  'properties': {
     'styles': 'true',
     'startIndex': 'true',
     'reversed': 'true',
  }
}
}

# Define a constant in settings.py to specify file upload permissions
CKEDITOR_5_FILE_UPLOAD_PERMISSION = "authenticated"  # Possible values: "staff", "authenticated", "any"

CKEDITOR_5_USER_LANGUAGE=True #使用Django配置的语言

6.修改项目的urls.py,如下所示:

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

urlpatterns = [
    path('admin/login/', sign_in, name='admin_login'),  #替代admin原始登录界面
    path('admin/', admin.site.urls),
    ......
]

urlpatterns += [
path("ckeditor5/", include('django_ckeditor_5.urls')),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
  1. 在项目应用(假设为myapp)的models.py中新建CKEditor类:

    from django.db import models
    from django_ckeditor_5.fields import CKEditor5Field

    class CkeditorArt(models.Model):
    #content = models.TextField(verbose_name='内容')
    article_id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=200,verbose_name='标题',default='CKEditor编辑页面')
    content = CKEditor5Field('内容',config_name='extends')

     #定义模型在admin管理界面显示名称,也可在admin.py中新建ModelAdmin类使用list_display来设置
     def __str__(self):
         return f"{self.title},{self.content}"
    
  2. 在项目应用(myapp)的forms.py中新建表单类:

    from django import forms
    from django_ckeditor_5.widgets import CKEditor5Widget
    from .models import CkeditorArt

    class PostAdminForm(forms.ModelForm):
    def init(self, *args, **kwargs):
    super().init(*args, **kwargs)
    self.fields["content"].required = False

     title = forms.CharField(label='文章标题',max_length=200, required=True, widget=forms.TextInput(attrs={"placeholder": "在这里输入标题",'style': 'width: 500px;'}),)
     class Meta:
         model = CkeditorArt
         fields = ('title','content')
         widgets = {
         "content": CKEditor5Widget(
             attrs={"class": "django_ckeditor_5"}, config_name="extends"
         )
     }
    

此处的CKEditor的配置config_name为前面setttings.py中设置extends配置。

  1. 为便于使用Django后台管理CKEditor表单提交的内容,在项目应用(myapp)的admin.py中增加如下内容:

    from .models import CkeditorArt
    class CkeditorArtAdmin(admin.ModelAdmin):
    list_display = ('title','content')
    admin.site.register(CkeditorArt,CkeditorArtAdmin)

  2. 更新数据库和static文件

    python3 manage.py makemigrations
    python3 manage.py migrate
    python3 manage.py collectstatic

  3. 在项目应用(myapp)的urls.py中设置路径:

    from django.urls import path
    from . import views

    urlpatterns = [
    path('Ckeditor/', views.Ckeditor, name='ckeditor'),
    .....
    ]

12.在项目应用(myapp)的views.py中新建上面提到的view函数Ckeditor:

from django.shortcuts import render
from django.http import HttpResponse
from .forms import PostAdminForm

@login_required(login_url='/login/')  #需要登录用户权限
def Ckeditor(request):
    """ 自定义form表单 """
    if request.method == 'POST':
        form = PostAdminForm(data=request.POST)
        if form.is_valid():
            form.save()
            return render(request, 'form-post-finished.html')
    form = PostAdminForm()
    return render(request, 'ckeditor-form.html', {'form':form})

13.在项目应用(myapp)的templates目录下新建上面提到的ckeditor-form.html,主要内容如下:

{% extends "newdesign/newbase.html" %}

    {% block mytitle %}  
        <title>Ckeditor富文本编辑</title>
    {% endblock %}

    {% block maincontent %}
    <div class="row">
        <form method="post", class="form-horizontal">
            {% csrf_token %}
          <p>标题: {{form.title |safe}}</p>
          <p>文章内容:</p> 
            {{form.content |safe}}
            {{form.media}}
            <input type="submit" value="Submit">
        </form>
    </div>       
    {% endblock %}

通过地址/myapp/Ckeditor即可访问CKEditor编辑页面,可以直接把word排版好的内容拷贝过来,格式和照片等都可以按word的排版正常显示。

  1. 在CKEditor表单页面输入文章标题,完成文章内容,示例如下,然后submit提交。

提交后可以在Django的管理后台看到相关记录:

15. 下面将所有文章以列表的形式在网页上展示出来,点击列表中文章的标题,即可展示文章内容,效果如下:

(1)在项目应用(myapp)的urls.py中设置bloglist和每篇文章的路径:

from django.urls import path
from . import views


urlpatterns = [
    path('Ckeditor/', views.Ckeditor, name='ckeditor'),
    path('bloglist/', views.Bloglist, name='bloglist'),   
    path('blog/<str:article_id>/', views.Showblog, name='showblog'),   
]

(2)在项目应用(myapp)的views.py中新建上面提到的view函数Bloglist和Showblog:

from .models import CkeditorArt
#@login_required(login_url='/login/')
def Showblog(request,article_id):
    try:
        article = CkeditorArt.objects.get(article_id=article_id)
        return render(request, 'showblog.html', {'content':article.content,'title':article.title})
    except CkeditorArt.DoesNotExist:
        message = '<h1 style="color: red;">文章没找到!<h1>'
        return render(request, 'showblog.html', {'content':message,'title':'文章没找到'})


#@login_required(login_url='/login/')
def Bloglist(request):
    #values返回字典,values_list返回元组
    objs = CkeditorArt.objects.values('article_id','title')
    context = {
    'objs':objs,
    }
    return render(request,'bloglist.html', context) 

(3)在项目应用(myapp)的templates目录下新建上面提到的bloglist.html和showblog.html

bloglist.html

{% extends "newdesign/newbase.html" %}

        {% block mytitle %}
            <title>BLOG列表</title>
        {% endblock %}

       {% block maincontent %}  
        <h1>这是CkEditor编辑的BLOG清单</h1>
        <div>
            <ul>
                {% for obj in objs %}
                <h5>文章{{obj.article_id}}: <a href="/myapp/blog/{{ obj.article_id }}/">{{obj.title}}</a></h5>
                {% endfor %}
            </ul>
        </div>
    {% endblock %}

showblog.html

{% extends "newdesign/newbase.html" %}

    {% block mytitle %}  
        <title>{{title}}</title>
    {% endblock %}

    {% block maincontent %}

    <h2>{{title}}</h2>

    {{content|safe}}
    {% endblock %}

至此,通过CKEditor就基本实现了一个简单博客网站的功能。

相关推荐
book01218 分钟前
MySql数据库运维学习笔记
运维·数据库·mysql
纠结哥_Shrek10 分钟前
Oracle和Mysql的区别
数据库·mysql·oracle
极客先躯11 分钟前
说说高级java每日一道面试题-2025年2月13日-数据库篇-请说说 MySQL 数据库的锁 ?
java·数据库·mysql·数据库的锁·模式分·粒度分·属性分
做梦敲代码12 分钟前
达梦统计信息
数据库·达梦数据库
jiugie1 小时前
MongoDB学习
数据库·python·mongodb
hzulwy1 小时前
MongoDB应用设计调优
数据库·mongodb
我爱松子鱼1 小时前
MySQL 单表访问方法详解
数据库·mysql
我们的五年1 小时前
MySQL存储引擎:选择与应用
数据库·mysql
带娃的IT创业者2 小时前
《Python实战进阶》专栏 No.3:Django 项目结构解析与入门DEMO
数据库·python·django
人间打气筒(Ada)2 小时前
MySQL优化
数据库·mysql