但是我使用ckeditor.create()就有代码块功能,这是为什么?我该如何解决没有代码块插入功能的问题?
方便提供一下可以复现问题的最小示例吗?用纯文本发出来,类似这样:
from flask import Flask, render_template
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired
from flask_ckeditor import CKEditor, CKEditorField
app = Flask(__name__)
app.config['CKEDITOR_SERVE_LOCAL'] = True
app.config['CKEDITOR_HEIGHT'] = 400
app.secret_key = 'secret string'
ckeditor = CKEditor(app)
class PostForm(FlaskForm):
title = StringField('Title')
body = CKEditorField('Body', validators=[DataRequired()])
submit = SubmitField('Submit')
@app.route('/', methods=['GET', 'POST'])
def index():
form = PostForm()
if form.validate_on_submit():
title = form.title.data
body = form.body.data
# WARNING: use bleach or something similar to clean the data (escape JavaScript code)
# You may need to store the data in database here
return render_template('post.html', title=title, body=body)
return render_template('index.html', form=form)
运行.py
from flask_sqlalchemy import SQLAlchemy
from flask import Flask,render_template,request,redirect,url_for,flash
from flask_ckeditor import CKEditor,CKEditorField
from flask_wtf import CSRFProtect
from 配置 import Config
from datetime import datetime
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired, Length
app = Flask(__name__)
app.config.from_object(Config)
db = SQLAlchemy()
ckeditor = CKEditor(app)
csrf = CSRFProtect(app)
db.init_app(app)
class 文章表(db.Model):
__tablename__ = 'Article' # 表名应与数据库中的表名一致
post_id = db.Column(db.Integer, primary_key=True, comment='文章id')
post_title = db.Column(db.String(100), nullable=False, comment='文章标题')
post_content = db.Column(db.Text, nullable=False, comment='文章内容')
post_author = db.Column(db.String(100), nullable=True, comment='文章作者')
post_category = db.Column(db.String(100), nullable=True, comment='文章分类,例如python,html,前端,后端')
post_tag = db.Column(db.String(100), nullable=True, comment='文章标签')
post_status = db.Column(db.String(100), nullable=True, comment='文章状态,例如发布 草稿')
post_link = db.Column(db.String(100), nullable=True, comment='文章链接')
# 确保导入 datetime 模块
post_published_date = db.Column(db.DateTime, default=datetime.now, comment='文章发布日期')
post_updated_date = db.Column(db.DateTime, default=datetime.now, onupdate=datetime.now, comment='文章更新时间')
def __repr__(self):
return f'<文章 {self.post_id}>'
def delete_data(self):
db.session.delete(self)
db.session.commit()
class 文章表单(FlaskForm):
title = StringField('文章标题',id='文章标题名',render_kw={'class': '文章标题名 form-control','placeholder':'文章标题'},validators=[DataRequired(),Length(1,100)])
author = StringField('作者',id='文章作者名',render_kw={'class': '文章作者名 form-control'},validators=[DataRequired(),Length(1,50)])
body = CKEditorField('正文',id='body 文章正文',validators=[DataRequired(),Length(1,5000)])
submit = SubmitField('发布',id='发布文章',render_kw={'class': '发布文章 btn btn-primary'})
class 更新文章表单(FlaskForm):
title = StringField('文章标题',id='文章标题名',render_kw={'class': '文章标题名 form-control','placeholder':'文章标题'},validators=[DataRequired(),Length(1,100)])
body = CKEditorField('正文',id='文章正文',validators=[DataRequired(),Length(1,5000)])
submit = SubmitField('更新',id='更新文章',render_kw={'class': '更新文章 btn btn-primary'})
@app.before_request
def create_tables():
try:
db.create_all()
except Exception as e:
print(f"An error occurred while creating tables: {e}")
@app.route('/',methods=['GET','POST'])
def index():
if request.method=='POST':
作者 = '1'
form = 文章表单() # 创建表单对象
if form.validate_on_submit(): # 如果表单通过验证
新文章 = 文章表(
post_title=form.title.data,
post_content=form.body.data,
post_author=作者,
post_updated_date=datetime.now()
)
db.session.add(新文章)
db.session.commit()
flash('文章已添加')
return redirect(url_for('index'))
return render_template('写文章.html',form=form)
@app.route('/all_post',methods=['GET','POST'])
def all():
content=文章表.query.all()
return render_template('所有文章.html',content=content)
@app.route('/update_post/<int:post_id>', methods=['GET', 'POST'])
def update(post_id):
文章对象 = 文章表.query.filter_by(post_id=post_id).first()
更新文章对象=更新文章表单()
if 更新文章对象.validate_on_submit():
文章对象.post_title=更新文章对象.title.data
文章对象.post_content=更新文章对象.body.data
db.session.commit()
flash('文章已更新')
return render_template('更新文章.html', 文章对象=文章对象,更新文章对象=更新文章对象)
@app.route('/delete_post/<int:post_id>', methods=['GET', 'POST'])
def delete(post_id):
文章对象=文章表.query.filter_by(post_id=post_id).first()
if 文章对象 is not None:
文章对象.delete_data()
return redirect(url_for('index'))
return '删除文章失败'
if __name__ == '__main__':
app.run()
配置.py
# -*- coding:utf-8 -*-
class Config:
DEBUG = True
SQLALCHEMY_DATABASE_URI = 'sqlite:///sqlite.db'
SQLALCHEMY_TRACK_MODIFICATIONS = False
SECRET_KEY = 'soTmypvK6wTtIa9JdESq'# 用于会话加密
SQLALCHEMY_POOL_RECYCLE = 110
JSON_AS_ASCII = False #json数据正常显示
CKEDITOR_ENABLE_CSRF = True
CKEDITOR_SERVE_LOCAL = True # 使用内置的ckeditor.load()方法时,设置是否使用本地资源,默认从CDN加载
CKEDITOR_PKG_TYPE = 'full' # CKEditor资源包的类型,basic、standard和full中的一个
# CKEDITOR_BASE_PATH = '/ckeditor/'# CKEditor 安装路径
CKEDITOR_LANGUAGE = 'zh-hans' #设置CKEditor文本编辑器的语言,zh-hans:简体中文
# CKEDITOR_WIDTH = 800 # 编辑器宽度,单位为px
CKEDITOR_HEIGHT = 500 # 编辑器高度,单位为px
CKEDITOR_EXTRA_PLUGINS = [] # 在CKEditor中开启的额外扩展列表,对应的扩展需要被安装
CKEDITOR_ENABLE_CODESNIPPET = True # 设置是否开启codesnippet插件(插入代码块),需要安装对应插件
CKEDITOR_ALLOW_NONIMAGE_FILES = True # 开启文件上传
CKEDITOR_FILE_UPLOADER = '/static/upload' # 设置文件上传路径
更新文章,html
{% extends '后台基类.html' %}
{% block title %}
更新文章页面
{% endblock %}
{% block content %}
{% for message in get_flashed_messages() %}
<div class="alert alert-warning">{{ message }}</div>
{% endfor %}
<form method="post">
{{ 更新文章对象.csrf_token }}
{{ 更新文章对象.submit() }} <a href="{{ url_for('delete',post_id=文章对象.post_id) }}"><button type="button" class="btn btn-danger">删除</button></a>
{{ 更新文章对象.title(value=文章对象.post_title) }}
{{ ckeditor.create(value=文章对象.post_content,name='body') }}
{{ ckeditor.load() }}
{{ ckeditor.config(name='body') }}
</form>
{% endblock %}
所有文章.html
{% extends '后台基类.html' %}
{% block title %}所有文章{% endblock %}
{% block content %}
<div class="d-flex flex-row flex-wrap gap-2">
<p class="me-auto">标题</p>
<p class="me-auto">作者</p>
<p class="me-auto">分类</p>
<p class="me-auto">标签</p>
<p class="me-auto">发布状态</p>
<p class="me-auto">发布日期</p>
</div>
{% for item in content %}
{{ ckeditor.load_code_theme() }}
<div class="d-flex flex-row flex-wrap gap-2">
<p class="me-auto"><a href="{{ url_for('update',post_id=item.post_id) }}">{{ item.post_title }}</a></p>
<p class="me-auto">{{ item.post_author }}</p>
<p class="me-auto">{{ item.post_category }}</p>
<p class="me-auto">{{ item.post_tag }}</p>
<p class="me-auto">{{ item.post_published_date }}</p>
</div>
{% endfor %}
{% endblock %}
写文章.html
{% extends '后台基类.html' %}
{% block title %}编辑器页面{% endblock %}
{% block link %}{% endblock %}
{% block t1 %}创建文章{% endblock %}
{% block content %}
<form method="post">
{{ form.csrf_token }}
{{ form.author.label }}
{{ form.author() }}
{{ form.submit() }}
{{ form.title() }}
{{ form.body() }}
{{ ckeditor.load() }}
{{ ckeditor.config(name='body') }}
</form>
{% endblock %}
后台基类.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>{% block title %}网站标题{% endblock %}</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
</head>
<body>
<nav class="navbar navbar-expand-lg bg-body-tertiary">
<div class="container-fluid">
<a class="navbar-brand" href="{{url_for('index')}}">写文章</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="{{url_for('all')}}">所有文章</a>
</li>
</ul>
</div>
</div>
</nav>
{% block content %}{% endblock %}
</body>
</html>
项目结构
博客/
__init__.py
运行.py
配置.py
templates/
写文章.html
后台基类.html
所有文章.html
更新文章.html
依赖库
requirements.txt
bleach==6.1.0
blinker==1.8.2
click==8.1.7
colorama==0.4.6
flask==3.0.3
Flask-CKEditor==1.0.0
flask-sqlalchemy==3.1.1
flask-wtf==1.2.1
greenlet==3.0.3
importlib-metadata==8.2.0
itsdangerous==2.2.0
jinja2==3.1.4
MarkupSafe==2.1.5
PyMySQL==1.1.1
six==1.16.0
SQLAlchemy==2.0.31
typing-extensions==4.12.2
webencodings==0.5.1
werkzeug==3.0.3
wtforms==3.1.2
zipp==3.19.2
使用Python解释器版本3.8.5
你这个最小示例也太大了。我在本地没法复现问题, 和是否使用 form 没有关系。仓库里这个示例就是使用的 form:
建议再排查下你代码的其他部分。
我懂了,在创建文章的页面我使用的是flask-wtf的form表单提供的form.body(),在更新文章界面我使用的是ckeditor.create().
只要在创建页面也使用ckeditor.create()即可,
同时可以指定当前ckeditor.create()生成html后的标签的name属性值,方便flask-wtf表单自动取值和验证.