前言:环境一致是复现代码的前提。
本次实验环境:python 3.7,Django 3.2.21,django-ckeditor 6.3.2注:请看到末尾
学习到django-ckeditor富文本这一节时,按照步骤:
-
pip install django-ckeditor
-
settings.py中配置:
INSTALLED_APPS = [ 'show', # 你的App 'ckeditor', # 普通富文本框 'ckeditor_uploader' # 可以上传文件的富文本框 ] MEDIA_URL = "/media/" MEDIA_ROOT = BASE_DIR / "media" CKEDITOR_UPLOAD_PATH = "article_images" CKEDITOR_CONFIGS = { 'default': { 'toolbar': 'Full' } } CKEDITOR_ALLOW_NONIMAGE_FILES = False # 不允许非图片文件上传: 即只能上传图片 CKEDITOR_BROWSE_SHOW_DIRS = True
别忘了新建文件夹/media/article_images,media与应用文件夹show同级
-
配置路由(关于总url中富文本编辑器的路由信息,请参考文末进行修正!)
# 总urls.py urlpatterns = [ path('admin/', admin.site.urls), path('', include('show.urls')), # 媒体资源路由 re_path('media/(?P<path>.*)', serve, {'document_root': settings.MEDIA_ROOT}, name='media'), # 设置编辑器的路由信息 path('ckeditor/', include('ckeditor_uploader.urls')), ]
-
更改模型字段
# show应用的models.py class ArticleInfo(models.Model): content = RichTextUploadingField(verbose_name='内容')
-
在admin后台注册编辑文章的入口:
# show应用的admin.py from django.contrib import admin from .models import * @admin.register(ArticleInfo) class ArticleInfoAdmin(admin.ModelAdmin): list_display = ['content']
-
数据迁移,创建superuser账户,登录后台,发现content字段并没有变成富文本框(RichTextUploadingField不行 ,但是普通富文本框RichTextField能正常显示,不过我需要的是可以上传文件的前者)
**
解答:
大多数答案都没有标注环境版本,找了很久才找到线索:How to add CkEditor in django
降低版本: pip install django-ckeditor==6.0.0 成功显示富文本框,并且能成功上传文件
在寻找解决方案的过程中,我还做了以下尝试并找到了另一种更复杂的解决方法:
注:原工程有点庞大,为了缩短篇幅,现将django-ckeditor所涉及到的代码单独挑出来做成demo(去掉自定义登录注册等功能)
思路:不降低django-ckeditor的版本,使用模型表单
很多答案都提到了表单,反正前端也要做一个文本编辑框让普通用户写博客,何不用模型表单?
-
安装django-ckeditor(同上)
-
settings.py配置(同上)
-
定义model(同上)
-
定义forms
from django import forms # from ckeditor.widgets import CKEditorWidget # 普通富文本框 from ckeditor_uploader.widgets import CKEditorUploadingWidget # 可以上传文件的富文本框 from .models import ArticleInfo class PostAdminForm(forms.ModelForm): # content = forms.CharField(widget=CKEditorWidget()) # 普通富文本框 content = forms.CharField(widget=CKEditorUploadingWidget()) # 可以上传文件的富文本框 class Meta: model = ArticleInfo fields = '__all__'
-
两个urls (关于总url中富文本编辑器的路由信息,请参考文末进行修正!)
# 总urls.py 参考文末进行修改 ... # myapp.urls.py urlpatterns = [ path('h.html', article, name='article'), ]
-
views.py
from django.shortcuts import render from .forms import PostAdminForm from django.http import HttpResponse def article(request): if request.method == 'POST': form = PostAdminForm(data=request.POST) if form.is_valid(): form.save() return HttpResponse('提交完了') form = PostAdminForm() return render(request, 'article.html', locals())
-
article.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>haha</title> </head> <body> <form method="post"> {% csrf_token %} {{form.content |safe}} {{form.media}} <input type="submit" value="Submit"> </form> </body> </html>
注意加上{{form.media}},根据文档,这句相当于下面手动加载外部富文本框资源。不加载资源无法正常显示
{% load static %} <script type="text/javascript" src="{% static "ckeditor/ckeditor-init.js" %}"></script> <script type="text/javascript" src="{% static "ckeditor/ckeditor/ckeditor.js" %}"></script>
-
admin.py
from django.contrib import admin from .forms import * from .models import * @admin.register(ArticleInfo) class ArticleInfoAdmin(admin.ModelAdmin): list_display = ['content'] form = PostAdminForm #注意!! 6.3.2版本下,RichTextUploadingField要在此处指定form才能在后台正常显示 ,而RichTextField不需要指定 # 如果降低了版本为6.0.0,都不需要指定form
-
数据迁移,创建superuser,登录admin后台,功能一切正常;访问前端界面http://127.0.0.1:8000/h.html,功能正常。
RichTextField和RichTextUploadingField两者的切换别忘了数据对应跟着迁移。
走的弯路:
由于先实验的普通富文本框,实验成功后,再换为能上传文件的富文本框。切换过程中,忘记将模型表单字段也由widget=CKEditorWidget()
更改为widget=CKEditorUploadingWidget()
,导致切换后不出现upload按钮
刚开始没查出原因,参考1 和 参考2,更改config.js文件,添加配置
config.filebrowserImageUploadUrl= "/media/article_images";
或者
config.filebrowserImageUploadUrl="/media/article_images/&responseType=json";
都无效
为了弄清楚config.filebrowserImageUploadUrl
的含义和用法,参考官方文档
**
如果没有设置,就用filebrowserUploadUrl
,如果设置了这个参数,upload按钮就会出现
查看源代码,默认会设置设置这个参数啊(如果确定用的是’ckeditor_uploader’的话)
# ...\venv\Lib\site-packages\ckeditor_uploader\widgets.py
from django.urls import reverse
from ckeditor import widgets
class CKEditorUploadingWidget(widgets.CKEditorWidget):
def _set_config(self):
if "filebrowserUploadUrl" not in self.config:
self.config.setdefault("filebrowserUploadUrl", reverse("ckeditor_upload"))
if "filebrowserBrowseUrl" not in self.config:
self.config.setdefault("filebrowserBrowseUrl", reverse("ckeditor_browse"))
super(CKEditorUploadingWidget, self)._set_config()
更有甚者(Enable Image Upload Button in ckeditor in Python/Django)让我去更改upload按钮的‘hidden’属性。upload按钮在ckeditor面板中隐藏没错,在ckeditor_uploader应该会自动显示出来啊,为什么需要手动更改?除非我用得根本就还是ckeditor(事实证明,确实如此)
查缺补漏:几个路径配置的用法与区别
富文本框不能显示,最先想到的原因是路径问题,检查了几个‘static’路径,更改半天都无效,最后发现这里根本就没用到static路径。。为了加深记忆,做个笔记吧~
STATIC_URL = '/static/' # 只能识别APP下的static文件夹
STATICFILES_DIRS = [BASE_DIR / 'publicStatic'] # 上面的升级版:资源集合,上线生产过程前需要去掉
STATIC_ROOT = BASE_DIR / 'static' # 上线生产阶段使用,服务器和项目资源映射,collectstatic命令收集所有的静态资源放这里
# 设置媒体资源的保存路径
MEDIA_URL = "/media/" # 代表用户通过URL来访问这个本地地址的URL。如本机http://127.0.0.1/, MEDIA_URL设置为"/site_media/",那么通过http://127.0.0.1/site_media/*** 就可以访问相关的上传图片或者其他资源。
MEDIA_ROOT = BASE_DIR / "media" # 用户上传的文件保存位置
次日更新:来看看我又发现了什么好问题
次日来重新操作一遍上传文件,发现普通用户在前端上传图片报错Incorrect server response.
把cookie清除了也不行,superuser登录admin后台中上传图片一切正常,但是普通用户前端就是上传不了图片!
参考django ckeditor image upload搞清楚原来默认上传文件有个职员身份限制!搞定!看来上传图片需要用户登录。
等等,我的demo不是为了节省篇幅而把自定义登录注册删了吗?不登陆为什么可以上传?实验一番,发现原来admin后台登录后,直接返回到前端富文本界面,操作此界面的用户还是刚才admin登录的用户! 在views.py的视图函数article中,打印 登录admin后访问前端富文本界面的request.user,和admin logout 后访问前端界面的request.user,验证猜想!
至于昨天为什么没发现错误,是因为我在admin后台登录了具有最高权限的superuser(不存在职员身份限制问题),并且不知道后台登录的superuser可以影响到前端(“不登录”还可以上传文件的问题),而superuser也一直没有logout,歪打正着,一路畅通。今天换成普通用户,就发现问题了。(仔细想想,富文本框中上传的文件属于文章信息表的content字段,文章信息表又和User表有关联,如果是匿名用户,上传的内容属于谁呢?)
# 总urls.py
...
from django.contrib.auth.decorators import login_required
from django.views.decorators.cache import never_cache
from ckeditor_uploader import views as ckeditor_views
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('show.urls')),
re_path('media/(?P<path>.*)', serve, {'document_root': settings.MEDIA_ROOT}, name='media'),
# 设置编辑器的路由信息
# path('ckeditor/', include('ckeditor_uploader.urls')),
path('ckeditor/upload/', login_required(ckeditor_views.upload), name='ckeditor_upload'),
path('ckeditor/browse/', never_cache(login_required(ckeditor_views.browse)), name='ckeditor_browse'),
]