吐槽一波,网上的教程都是小可爱。都他喵的是抄的,关键是还抄不好,害的我踩了不少坑。而且现在关于这方面从我目前查的资料来看都不全面,基本上都只是在后端使用markdown编辑器然后在前端显示,但是开玩笑,后端用只能做个个人博客网站用,那玩意django自带一个要你写有毒啊?而且所谓的教程连个案例都没有,还得我自己去找找,去看看官方文档什么的。然后就是那个django网上大部分的教程用的插件是django-mdeditor,但是这玩意其实只是用于后端页面的markdown编辑器显示的。如果要玩前端的编辑器我们应该玩的是editor.md这一个开源的适用于前端的markdown编辑器,那个插件也是基于这个来玩的。这里的话做个全面一点的总结,没办法鄙人要搞个技术交流博客社区,不得不用这个,如果用富文本编辑器的话,虽然很成熟,但是用起来和shit
一样。
最后本文是本博主踩坑制作,也是white Hole的开发历程之一 White Hole 详情。
一定要仔细看此博文,被坑了不要怪我没提醒
想要用那必然是要安装相应的环境。那么这里准备安装四个玩意
1.pip install django-mdeditor # 用于后台编辑
2.pip install markdown # 用于前端显示
3.pip install Pygments # 用于前端显示markdown里面写的代码的高亮
4.editor.md 开源的前端的编辑器的源码,这个在GitHub上,下载解压(好像也有官网)
首先前面那三个玩意,直接下载就好了,怎么配置待会说。
后面那个请移步官网下载,解压,然后放到你django项目的static文件下面。例如:
这个是editor.md的源码
这个是真的简单,主要使用的就是django-mdeditor这个插件,接下来分几个小步。
首先找到settings然后注册。
这个是那个django-mdeditor插件的app
然后再加入下面几个配置
MEDIA_ROOT = os.path.join(BASE_DIR, 'uploads') #新建一个uploads文件夹,且在项目目录下
MEDIA_URL = '/media/' #你上传的文件和图片会默认存在/uploads/editor下 这一步不用创建任何文件夹,这个是那个插件帮我们做好了,做好了一个图片上传,后面我们自己在前端做编辑器的话我么还要自己再实现一个。
接下来注册一个路由就好了。找到你的路由文件,写入如下代码。
urlpatterns = [
path('admin/',admin.site.urls),
url(r'mdeditor/', include('mdeditor.urls')),
]
if settings.DEBUG:
# static files (images, css, javascript, etc.)主要是让页面显示正常
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
首先创建一个app或者在你的app里面的modules里面创建模型。
class ExampleModel(models.Model):
name = models.CharField(max_length=10)
content = MDTextField()
这个是官方文档里面的一个示例。
之后迁移注册在admin里面(在你APP的admin.py里面)
from django.contrib import admin
from . import models
admin.site.register(models.ExampleModel)
接下来就是那个验证了,进入后台。
进入后台编辑
注册一个路由,然后编写view就不要复述了吧。
现在数据库已经把博客写进去了。
先激活一下Pygments,生成这个css渲染文件
进入你想要放css文件的地方在终端执行:
pygmentize -S default -f html -a .codehilite > code.css
那个default只是一个样式
此外支持的样式还有
default
emacs
friendly
colorful
autumn
murphy
manni
monokai
perldoc
pastie
borland
trac
native
fruity
bw
vim
vs
tango
rrt
xcode
igor
paraiso-light
paraiso-dark
lovelace
algol
algol_nu
arduino
rainbow_dash
abap
我这里用的是monokai
pygmentize -S monokai -f html -a .codehilite > monokai.css
到这里就可以编写模板了
DOCTYPE html>
<html lang="en">
{% load static %}
<head>
<meta charset="UTF-8">
<title>Showtitle>
<link rel="stylesheet" type="text/css" href="{% static 'md_css/monokai.css' %}">
head>
<body>
<div>
{
{ article.content|safe }}
div>
body>
html>
然后编写我们的view
def show(request):
article = ExampleModel.objects.get(id=2)#我这里这是做个显示写死了
# 将markdown语法渲染成html样式
article.content = markdown.markdown(article.content,
extensions=[
'markdown.extensions.extra',
'markdown.extensions.codehilite',
'markdown.extensions.toc',
])
context = {
'article': article}
return render(request, 'blogedit.html', context)
markdown.extensions.extra
:用于标题、表格、引用这些基本转换
markdown.extensions.codehilite
:用于语法高亮
markdown.extensions.toc
:用于生成目录
效果如下:
全屏可能不好看那就改改div布局。
重点来了。
这个我是直接用editor.md做的,其实这些插件都是基于这个来做的,毕竟开源的东西嘛。
我这里的话直接把我正在做的博客编辑页面的代码拿过来,反正那个做好了也是开源的。
{% load static %}
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Markdown Editortitle>
<link rel="stylesheet" href="{% static 'mdeditor/css/editormd.css' %}"/>
head>
<style>
#center {
margin-top: 5%;
width: 96%;
height: 96%;
border: 1px;
}
img {
margin: auto;
margin-left: 30%;
height: 40%;
width: 40%;
position: relative;
top: 10%;
}
input {
width: 85%;
height: 30px;
border-width: 2px;
border-radius: 5px;
border-color: #00c4ff;
border-bottom-color: #2C7EEA;
color: #586e75;
font-size: 15px;
}
button {
width: 10%;
height: 35px;
border-width: 0px;
margin-left: 3%;
border-radius: 10px;
background: #1E90FF;
cursor: pointer;
outline: none;
font-family: Microsoft YaHei;
color: white;
font-size: 17px;
}
button:hover {
background-color: #1E90FF;
box-shadow: 0 4px 0 powderblue;
}
style>
<body>
{% csrf_token %}
<form method="post" action="{% url "save" %}" enctype="multipart/form-data">
<div class="form-group">
<div>
<input type="text" name="blogname" placeholder="请输入文章标题" required>
<button type="submit" id="submit">发布文章button>
div>
<br>
<div id="editormd">
{% csrf_token %}
<textarea style="display:none;" name="blogbody" id="blogeditor">textarea>
div>
div>
form>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js">script>
<script src="{% static 'mdeditor/editormd.min.js' %}">script>
<script type="text/javascript">
$(function () {
var editor = editormd("editormd", {
width : "100%",
height : "800",
path : "{% static 'mdeditor/lib/' %}",
placeholder: "请开始你的书写之旅",
//增加图片上传配置
imageUpload : true,
imageFormats : ["jpg", "jpeg", "gif", "png", "bmp", "webp"],
imageUploadURL : "/upload/",//图片上传的地址(在你的项目里面的路由)
required:true,
saveHTMLToTextarea : true,
taskList: true,
tocm: true,
tex: true, // 开启科学公式TeX语言支持,默认关闭
flowChart: true, // 开启流程图支持,默认关闭
sequenceDiagram: true, // 开启时序/序列图支持,默认关闭,
});
});
var blogeditor = document.getElementById("blogeditor");
blogeditor.required = true;
var button = document.getElementById("submit");
button.addEventListener("click",()=>{
if(blogeditor.value==''){
alert("博客内容不能为空");
}
});
script>
body>
html>
这个你们自己看着改。
这个也是比较重要的功能
这里我加入一个上传本地图片的功能。
这里再明确一下我们使用的是editor.md这个开源的玩意,所以我们的图片上传功能也要基于这个。
那么首先做一个图片上传功能其实很简单,按照我前面给的前端模板就可以看到我已经开启了,现在前端模板不用管,把视线
移步到后端即可。
首先找到项目的static文件,里面创建一个文件夹用来保存你的图片。
由于前端已经搞好了,那么我们后端要做的就只有一个保存图片,然后告诉前端,地址在哪。
首先来看看我们应该注意的参数
我们保存图片后需要返回一个json数据
{
success : 0 | 1, // 0 表示上传失败,1 表示上传成功
message : "提示的信息,上传成功或上传失败及错误信息等。",
url : "图片地址" // 上传成功时才返回
}
现在我们是上传文件一个是POST跨站Django需要验证,还有一个是文件上传也要验证,由于不方便直接在前端加入crsf_token。所以我在后端加入,此外文件的也是。
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.clickjacking import xframe_options_sameorigin
import os
import time
import uuid
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
@csrf_exempt
@xframe_options_sameorigin
def upload(request):
if request.method == "POST":
obj = request.FILES.get('editormd-image-file')
file_name = time.strftime('%Y%m%d%H%M%S') + str(uuid.uuid1().hex) + '.' + obj.name.split('.')[-1] # 图片文件名
data_path = time.strftime('%Y%m')
print(data_path)
dir_path = os.path.join(BASE_DIR, 'static', 'upload', data_path) # 保存的文件目录
if not os.path.exists(dir_path):
os.makedirs(dir_path)
img_path = os.path.join(dir_path, file_name) # 存储的完整图片路径(绝对路径)
f = open(img_path, 'wb')
for chunk in obj.chunks():
f.write(chunk)
f.close()
data = {
"success": 1, "message": "上传成功", "url": '/static/upload/' +data_path+'/'+file_name}
return JsonResponse(data)
else:
return JsonResponse({
"success": 0, "message": "上传失败"})
此外关于文件上传的话还有一个解决办法,直接在设置里面设置允许,但是那个是全局的。你只需要在项目的settings加入
X_FRAME_OPTIONS = 'SAMEORIGIN'
之后就是这个文件上传的话,本来是打算用django自带的解决方案的,但是这里要返回保存地址,所以就没有办法直接用了。因为要保证文件名不重复只能这样搞个随机文件名,此外在Linux系统里面一个文件夹只能放65535张图片
这个其实还是很简单的,但是让我很难受的是网上的一堆垃圾教程,啥也说不明白,到处都是坑,根本跑不了,很多东西都是我直接在浏览器debug出来的,就问你恶不恶心。那么之后就是建表,弄模型就好了,至于显示都是一样的。对了忘了说了,那就是在admin的那个编辑器里面想要使用上传图片也要设置一下django的允许上传,能不能指定admin那我也不清楚,不过你可以直接在settings里面去设置那个上传验证的,不过那是全局的。如果你知道记得踹我!累了不想搞了。