Flask-CKEditor为啥使用form.body就没有插入代码块的功能?

但是我使用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表单自动取值和验证.