Django 富文本编辑器Ckeditor

196 阅读5分钟

Ckeditor

admin使用Ckeditor富文本编辑器

富文本编辑器有很多种,这里介绍如何使用ckeditor

  • 安装ckeditor
pip install django-ckeditor
  • settings配置
#settings.py
NSTALLED_APPS = [
    ...
    'ckeditor',  # 富文本编辑器
    'ckeditor_uploader',  # 富文本编辑器上传图片模块
    ...
]
  • 添加ckeditor配置
#settings.py
CKEDITOR_CONFIGS = {
    'default':{
        'toolbar':'full', # 完整工具条
        'height': 300, # 编辑高度
        # 'woidth': 300, # 编辑宽度
    },
}
CKEDITOR_UPLOAD_PATH = ''   # 上传图片保存路径,使用了fastDFS,设置为''
  • 添加ckeditor路由配置
#urls.py
urlpatterns = [
    ...
    re_path(r'^ckeditor/', include('ckeditor_uploader.urls')),
]
  • 为我们的商品SPU表种设置额外的三个字段
from ckeditor.fields import RichTextField
from ckeditor_uploader.fields import RichTextUploadingField
class Goods(BaseModel):
    ...
    desc_detail = RichTextUploadingField(default='', verbose_name='详细介绍')
    desc_pack = RichTextField(default='', verbose_name='包装信息')
    desc_service = RichTextUploadingField(default='', verbose_name='售后服务')
    ...
  • 解决ckeditor繁体中文

C:\Python37\Lib\site-packages\ckeditor\static\ckeditor\ckeditor\lang

修改其中的语言文件配置,把zh-ch文件内容复制到zh文件中即可

vue使用CKEditor富文本编辑器

  • 在vue中安装CKEditor
cnpm install ckeditor/ckeditor5-build-classic
  • 组件引入CKEditor
<script>
import CKEditor from "@ckeditor/ckeditor5-build-classic";
import "@ckeditor/ckeditor5-build-classic/build/translations/zh-cn"; //中文包
</script>
  • 模版标签嵌入
<template>
  <div id="toolbar-container"></div>	<!-- 编辑器容器 -->
  <div id="editor">
    <!-- <p>This is the initial editor content.</p> -->
  </div>
</template>
  • vue实例数据保存编辑器实例
data() {
    return {
     		editor: null, // 编辑器实例
    }
},
  • 钩子函数初始化CKEditor编辑器
mounted() {
  	this.initCKEditor();
},
methods: {
    initCKEditor() {
      CKEditor.create(document.querySelector("#editor"), {
        ckfinder: {
          uploadUrl: "http://127.0.0.1:8000/ck_upload/"
          // 接口返回需要:包括uploaded(选项true/false),url两个字段
        }
      })
        .then(editor => {
          const toolbarContainer = document.querySelector("#toolbar-container");
          toolbarContainer.appendChild(editor.ui.view.toolbar.element);
          this.editor = editor; //将编辑器保存起来,用来随时获取编辑器中的内容等,执行一些操作
        })
        .catch(error => {
          console.error(error);
        });
    },
}
  • 在django中响应图片上传的接口
import hashlib
def md5_(filename):
    m = hashlib.md5()
    m.update(filename.encode())
    return m.hexdigest()
# Create your views here.
def ck_upload(request):
  	_f = request.FILES.get('upload')
    _name = md5_(_f.name) # 生成md5名
    _path = os.path.join(STATICFILES_DIRS[0],_name)
    with open(_path,'wb') as fp:
        fp.write(_f.read())
    return JsonResponse({
        'uploaded':'true',
      	# 表示上传成功
        'url': 'http://127.0.0.1:8000/static/' + _name 
      	# 拼接上传图片的绝对访问路径
    })
  • 整个页面的提交,需要在vue组件template中绑定按钮点击时间
<template>
    <div>
        <div id="toolbar-container"></div>
        <div id="editor">
        </div>
        <button @click="post_editor">提交</button>
    </div>
</template>
  • post_editor的提交事件方法
ck_post() {
    axios.post("http://127.0.0.1:8000/ck_post/", {
      	data: this.editor.getData() // 获取了编辑器的所有内容
    }).then(res=>{
        console.log(res.data)
    })
}
  • 后端接收整个富文本部分的内容
class CkPost(APIView):
    def post(self,request):
        ck_data = request.data.get('data')
        ...
        return Response({'code':200})

验证码校验

  • 验证码逻辑
  1. 客户端发起GET连接请求,并随机生成UUID,绑定图片
    • UUID:通用唯一识别码(Universally Unique Identifier),目的,是让分布式系统中的所有元素,都能有唯一的辨识信息,每个人都可以创建不与其它人冲突的UUID
  2. 服务端生成图片验证码,图片存入内存并返回到客户端
  3. 服务端存储源字符串到session中,也可以存入缓存中,例memcachedredis
  4. 客户端表单填写验证码原值
  5. 移出表单框时间触发异步post请求验证,访问时,图片uuid作为属性绑定到表单属性中,作为post提交的数据一部分
  6. 服务端验证时通过UUIDkey,表单值为value进行图片验证码校验

图片验证码生成

利用pillpw库进行图片验证码的生成

from PIL import Image, ImageDraw, ImageFont
def generate(request, uuid):
    '''
        本地图片验证码生成函数
    '''
    bgcolor = (random.randrange(20, 100), random.randrange(
        20, 100), random.randrange(20, 100))
    width = 110
    height = 40
    # 创建画面对象
    im = Image.new('RGB', (width, height), bgcolor)
    # 创建画笔对象
    draw = ImageDraw.Draw(im)
    # 调用画笔的point()函数绘制噪点
    for i in range(0, 100):
        xy = (random.randrange(0, width), random.randrange(0, height))
        fill = (random.randrange(0, 255), 255, random.randrange(0, 255))
        draw.point(xy, fill=fill)
    # 定义验证码的备选值
    str = '1234567890QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm'
    # 随机选取4个值作为验证码
    rand_str = ''
    for i in range(0, 4):
        rand_str += str[random.randrange(0, len(str))]
    # 构造字体对象

    fonts_files = os.path.join(
        STATICFILES_DIRS[0], 'fonts/' + 'SourceCodePro-Bold.ttf')
    font = ImageFont.truetype(fonts_files, 30)
    # 构造字体颜色
    fontcolor1 = (255, random.randrange(0, 255), random.randrange(0, 255))
    fontcolor2 = (255, random.randrange(0, 255), random.randrange(0, 255))
    fontcolor3 = (255, random.randrange(0, 255), random.randrange(0, 255))
    fontcolor4 = (255, random.randrange(0, 255), random.randrange(0, 255))
    # 绘制4个字
    draw.text((5, 2), rand_str[0], font=font, fill=fontcolor1)
    draw.text((25, 2), rand_str[1], font=font, fill=fontcolor2)
    draw.text((50, 2), rand_str[2], font=font, fill=fontcolor3)
    draw.text((75, 2), rand_str[3], font=font, fill=fontcolor4)
    # 释放画笔
    del draw
    # 存入缓存,用于做进一步验证,并设置超时时间为10分组
    cache.set(uuid,rand_str,60*10)
    buf = io.BytesIO()
    # 将图片保存在内存中,文件类型为png
    im.save(buf, 'png')
    # 将内存中的图片数据返回给客户端,MIME类型为图片png!
    return HttpResponse(buf.getvalue(), 'image/png')

uuid生成

浏览器生成唯一标示uuid进行图片验证码的索取

// 根据时间戳生成当前UUID值
generate_uuid: function(){
    var d = new Date().getTime();
    if(window.performance && typeof window.performance.now === "function"){
        d += performance.now(); //use high-precision timer if available
    }
    var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
        var r = (d + Math.random()*16)%16 | 0;
        d = Math.floor(d/16);
        return (c =='x' ? r : (r&0x3|0x8)).toString(16);
    });
    return uuid;
},

Django生效Redis缓存

CACHES = {
    "default": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379/1",
      	"PASSWORD": "123456",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
        }
    }
}
  • 使用方法就很简单了
from django.core.cache import cache
cache.set
cache.get

服务端校验通用接口

  • 视图
# views.py
class ImageCodeView(APIView):
    def get(self, request, generate_image_id):
        '''
            获取验证码
        '''
        return generate_image_code(request,generate_image_id)

    def post(self, request,generate_image_id):
        '''
            验证码识别
        '''
        data = {}
        image_code = cache.get(generate_image_id) # 获取缓存中的验证码字符
        if image_code == request.data.get('image_code'):
            data['code'] = 200
        else:
            data['code'] = 201
        return Response(data)
  • 路由
#urls.py
re_path('generate_image_code/(?P<generate_image_id>)[\w-]+/',views.ImageCodeView.as_view(),name='generate_image_code'),