django 07
富文本编辑器
富文本编辑器,Rich Text Editor, 简称 RTE, 是一种可内嵌于浏览器,所见即所得的文本编辑器。
UEditor是由百度web前端研发部开发所见即所得富文本web编辑器,具有轻量,可定制,注重用户体验等特点,开源基于MIT协议,允许自由使用和修改代码。
1.首先下载富文本编辑器
下载链接: http://ueditor.baidu.com/website/download.html#ueditor
这里我们下载完整源码
2.将下载的内容放到django项目中,这里我们放在static文件夹下,新建一个ueditor文件夹,将其放入其中。
3.创建一个html
这里我们使用ueditor下_examples下submitFormDemo.html的模板样式,将b.html修改为
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" charset="utf-8" src="/static/ueditor/ueditor.config.js"></script>
<script type="text/javascript" charset="utf-8" src="/static/ueditor/_examples/editor_api.js"></script>
</head>
<body>
<form id="form" method="post" target="_blank">
{% csrf_token %}
<script type="text/plain" id="myEditor" name="myEditor">
<p>欢迎使用UEditor!</p>
</script>
<input type="submit" value="通过input的submit提交">
</form>
<script type="text/javascript">
var editor_a = UE.getEditor('myEditor',{});
</script>
</body>
</html>
这里需注意将我们导入的script文件修改成我们当前存放的路径
接着我们打开editor_api.js
将下面baseURL 的路径改为我们当前_src的路径:
这一点很重要,它的目的是将这些script文件导入到html里面
3. 视图函数与urls路由
我们将uecontroller.py 放入blog包中。
uecontroller.py:
from django.shortcuts import render , redirect , reverse , HttpResponse
import json
import re
configStr = ""
with open('config.json','r',encoding="utf-8") as jf:
for line in jf:
configStr += re.sub(r"/\*(.*)\*/","",line)
print(configStr)
config = json.loads(configStr)
print(config)
from django.http import HttpResponse
import codecs
import json
import os
from django.views.decorators.csrf import csrf_exempt
import random
from datetime import *
import blog.settings as settings
#ROOT = os.path.dirname(__file__)
ROOT = settings.BASE_DIR
#本地上传图片时构造json返回值
class JsonResult(object):
def __init__(self,state="未知错误",url="",title="",original="",error="null"):
super(JsonResult,self).__init__()
self.state = state
self.url = url
self.title = title
self.original = original
self.error = error
#构造返回json
def buildJsonResult(result):
jsondata = {"state":result.state,"url":result.url,"title":result.title,"original":result.original,"error":result.error}
return json.dumps(jsondata)
def buildFileName(filename):
dt = datetime.now()
name,ext = os.path.splitext(filename)
return dt.strftime("%Y%m%d%M%H%S{0}{1}".format(random.randint(1,999999),ext))
#读取json文件
def getConfigContent():
return config
#上传配置类
class UploadConfig(object):
def __init__(self,PathFormat,UploadFieldName,SizeLimit,AllowExtensions,SavePath,Base64,Base64Filename):
super(UploadConfig,self).__init__()
self.PathFormat = PathFormat
self.UploadFieldName = UploadFieldName
self.SizeLimit = SizeLimit
self.AllowExtensions = AllowExtensions
self.SavePath = SavePath
self.Base64 = Base64
self.Base64Filename = Base64Filename
#获取json配置中的某属性值
def GetConfigValue(key):
config = getConfigContent()
return config[key]
#检查文件扩展名是否在允许的扩展名内
def CheckFileType(filename,AllowExtensions):
exts = list(AllowExtensions)
name,ext = os.path.splitext(filename)
return ext in exts
def CheckFileSize(filesize,SizeLimit):
return filesize<SizeLimit
#处理上传图片、文件、视频文件
@csrf_exempt
def uploadFile(request,config):
result = JsonResult()
if config.Base64:
pass
else:
buf = request.FILES.get(config.UploadFieldName)
filename = buf.name
if not CheckFileType(filename,config.AllowExtensions):
result.error =u"不允许的文件格式"
return HttpResponse(buildJsonResult(result))
if not CheckFileSize(buf.size,config.SizeLimit):
result.error = u"文件大小超出服务器限制"
return HttpResponse(buildJsonResult(result))
try:
#重新定义上传图片的名字
truelyName = buildFileName(filename)
#
webUrl = config.SavePath+ truelyName
print(webUrl)
#savePath =ROOT+webUrl
savePath = R'static/uplaod/img/'+ webUrl
print(savePath)
f = codecs.open(savePath,"wb")
for chunk in buf.chunks():
f.write(chunk)
f.flush()
f.close()
result.state = "SUCCESS"
#返回富文本显示的图片地址
result.url = R'/'+savePath#truelyName
result.title = truelyName
result.original = truelyName
response = HttpResponse(buildJsonResult(result))
response["Content-Type"] = "text/plain"
return response
except Exception as e:
result.error = u"网络错误"
return HttpResponse(buildJsonResult(result))
#处理在线图片与在线文件
#返回的数据格式:{"state":"SUCCESS","list":[{"url":"upload/image/20140627/6353948647502438222009315.png"},{"url":"upload/image/20140627/6353948659383617789875352.png"},{"url":"upload/image/20140701/6353980733328090063690725.png"},{"url":"upload/image/20140701/6353980745691597223366891.png"},{"url":"upload/image/20140701/6353980747586705613811538.png"},{"url":"upload/image/20140701/6353980823509548151892908.png"}],"start":0,"size":20,"total":6}
def listFileManage(request,imageManagerListPath,imageManagerAllowFiles,listsize):
pstart = request.GET.get("start")
start = pstart==None and int(pstart) or 0
psize = request.GET.get("size")
size = psize==None and int(GetConfigValue(listsize)) or int(psize)
localPath = ROOT+imageManagerListPath
filelist = []
exts = list(imageManagerAllowFiles)
index = start
print(localPath)
for imagename in os.listdir(localPath):
name,ext = os.path.splitext(imagename)
if ext in exts:
filelist.append(dict(url=imagename))
index+=1
if index-start>=size:
break
jsondata = {"state":"SUCCESS","list":filelist,"start":start,"size":size,"total":index}
return HttpResponse(json.dumps(jsondata))
#返回配置信息
def configHandler(request):
content = getConfigContent()
callback = request.GET.get("callback")
if callback:
return HttpResponse("{0}{1}".format(callback,json.dumps(content)))
return HttpResponse(json.dumps(content))
#图片上传控制
@csrf_exempt
def uploadimageHandler(request):
AllowExtensions = GetConfigValue("imageAllowFiles")
PathFormat = GetConfigValue("imagePathFormat")
SizeLimit = GetConfigValue("imageMaxSize")
UploadFieldName = GetConfigValue("imageFieldName")
SavePath = GetConfigValue("imageUrlPrefix")
upconfig = UploadConfig(PathFormat,UploadFieldName,SizeLimit,AllowExtensions,SavePath,False,'')
return uploadFile(request,upconfig)
def uploadvideoHandler(request):
AllowExtensions = GetConfigValue("videoAllowFiles")
PathFormat = GetConfigValue("videoPathFormat")
SizeLimit = GetConfigValue("videoMaxSize")
UploadFieldName = GetConfigValue("videoFieldName")
SavePath = GetConfigValue("videoUrlPrefix")
upconfig = UploadConfig(PathFormat,UploadFieldName,SizeLimit,AllowExtensions,SavePath,False,'')
return uploadFile(request,upconfig)
def uploadfileHandler(request):
AllowExtensions = GetConfigValue("fileAllowFiles")
PathFormat = GetConfigValue("filePathFormat")
SizeLimit = GetConfigValue("fileMaxSize")
UploadFieldName = GetConfigValue("fileFieldName")
SavePath = GetConfigValue("fileUrlPrefix")
upconfig = UploadConfig(PathFormat,UploadFieldName,SizeLimit,AllowExtensions,SavePath,False,'')
return uploadFile(request,upconfig)
#在线图片
def listimageHandler(request):
imageManagerListPath = GetConfigValue("imageManagerListPath")
imageManagerAllowFiles = GetConfigValue("imageManagerAllowFiles")
imagelistsize = GetConfigValue("imageManagerListSize")
return listFileManage(request,imageManagerListPath,imageManagerAllowFiles,imagelistsize)
#在线文件
def ListFileManagerHander(request):
fileManagerListPath = GetConfigValue("fileManagerListPath")
fileManagerAllowFiles = GetConfigValue("fileManagerAllowFiles")
filelistsize = GetConfigValue("fileManagerListSize")
return listFileManage(request,fileManagerListPath,fileManagerAllowFiles,filelistsize)
actions = {
"config":configHandler,
"uploadimage":uploadimageHandler,
"uploadvideo":uploadvideoHandler,
"uploadfile":uploadfileHandler,
"listimage":listimageHandler,
"listfile":ListFileManagerHander
}
@csrf_exempt
def handler(request):
action = request.GET.get("action")
return actions.get(action)(request)
首先将settings的路径修改为django项目的路径
再将b.html中script添加:
然后在blog中的urls中添加一条路径:
#usr/bin/python
#-*-coding:utf-8-*-
app_name='blog'
from django.urls import path
from blog import views
from .uecontroller import handler
urlpatterns = [
path('saveblog', views.saveblog),
path('uecontroller', handler),
]
然后将ueditor下的jsp下的config.json 文件复制粘贴到根目录下,如图:
最后修改uecontroller.py中uploadFile方法中的路径:
这时打开b页面为这个样式:
上传图片可到:
现在我们更改saveblog.html页面,使其可向b页面一样实现富文本:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
p{
color: green;
}
</style>
<script type="text/javascript" charset="utf-8" src="/static/ueditor/ueditor.config.js"></script>
<script type="text/javascript" charset="utf-8" src="/static/ueditor/_examples/editor_api.js"></script>
</head>
<body>
<form method="post" action="saveblog">
{% csrf_token %}
<p>
请输入标题: <input type="text" name="title">
</p>
<p>
请输入内容: <textarea id="myEditor" name="content"></textarea>
</p>
<p>
<input type="submit" name="" value="发布内容">
</p>
{{ error }}
</form>
<script type="text/javascript">
var editor_a = UE.getEditor('myEditor',{serverUrl:'/blog/uecontroller'});
</script>
</body>
</html>
现在我们进入到saveblog页面即显示为:
之后我们创建一些博客数据,用来为后面的分页做准备。
要实现分页首先要导入一个模块:
from django.core.paginator import Paginator
然后在blog_user中的views里的welcome2重写函数:
from django.core.paginator import Paginator
from blog.models import Blog
#每次跳转welcome2页面都要查询某页的数据
def welcome2(request):
user = request.session.get('user')
blogSet=Blog.objects
#查询哪些数据 __双下划线代表表连接
blogSet = blogSet.values('id','title','create_date','blogUser__userName')
#获取所有数据
blogSet = blogSet.all().order_by('id')
#设置获取第几页的数据
pageNum = int (request.GET.get('pageNum',1))
#分页器
page=Paginator(blogSet,5)
#判断是否为第一页
isFirstPage=(pageNum==1)
#判断是否为最后一页 num_pages一共多少页
isLastPage=(pageNum==page.num_pages)
return render(request,'user/welcome2.html',
{'user':user,
'blogSet':page.get_page(pageNum),#当前页的数据
'pageNum':pageNum, #第几页数据
'ppageNum':pageNum-1, #前一页
'npageNum':pageNum+1, #后一页
'isFirstPage':isFirstPage, #是否为第一页
'isLastPage':isLastPage, #是否为最后一页
'totalPage':page.num_pages}) #总页数
上述函数为:首先创建了一个Blog模型blogSet,然后预查询了模型的id,标题,创建时间,并联合blogUser表查询了用户名(即为作者),又预查询了根据id排序的所有内容,设置pageNum为获取到的第几页(如果没有获取到pageNum,则为1),并将其转换为int类型,设置page为分页器,每5条博客为1页,若不足5条则也算作一页,接着设置了两个参数,判断当前是否为第一页或者是否为最后一页,并将这些参数传回到网页welcome2中。
现在在welcome2中显示分页结果并添加判断:
<p>
{% if not isFirstPage %}
<a href="welcome2?pageNum=1">首页</a>
<a href="welcome2?pageNum={{ ppageNum }}">上一页</a>
{% endif %}
[{{ pageNum }}/{{ totalPage }}]
{% if not isLastPage %}
<a href="welcome2?pageNum={{ npageNum }}">下一页</a>
<a href="welcome2?pageNum={{ totalPage }}">末页</a>
{% endif %}
</p>
?代表传参。
接着我们需要创建一个wenzhang.html页面用于存放博客内容,首先我们先来编写相关的路径及函数。
在blog中的urls中:path('wenzhang', views.wenzhang),
接着再views中:
def wenzhang(request):
b_id = request.GET.get('id',1)
blog = Blog.objects.get(pk=b_id)
return render(request,'blog/wenzhang.html',
{'blog':blog})
接着我们在templates新建一个文件夹blog,里面创建一个wenzhang.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>标题{{ blog.title }}</h1>
<h2>作者:{{ blog.blogUser.userName }}</h2>
<p>发布时间:{{ blog.create_date|date:'Y-m-d h:i:s' }}</p>
<div>
{% autoescape off %}
{{ blog.content }}
{% endautoescape %}
</div>
</body>
</html>
{% autoescape off %} {% endautoescape %}可以将html代码自动转译。
接着我们想在welcome2页面上显示每一页的数据:
{# 循环显示当前页的数据 #}
<ul>
{% for blog in blogSet %}
<li>
<a href="/blog/wenzhang?id={{ blog.id }}">{{ blog.title }}</a>
作者:<span>{{ blog.blogUser__userName }}</span>
创建时间:<span>{{ blog.create_date|date:'Y-m-d h:i:s' }}</span>
</li>
{% endfor %}
</ul>
blogSet为welcome2函数传进来的当前页面的数据;
| 为过滤器 上述使用的是将时间转换为年-月-日 时:分:秒的格式
打开welcome2页面:
随便点入一条博客显示: