我正在构建一个 Flask 博客应用程序并尝试将
CKEditor
集成到模板中。首先,我尝试使用 CDN
来实现它,效果很好。然后我使用 CKEditor 5 Online Builder 构建了一个自定义构建。我下载了 zip 文件,将其解压并将其放入 Flask 项目目录中的静态文件夹中。我将之前链接到 src
的脚本 CDN
更改为我的自定义 CKEditor 版本。执行此操作后,创建新帖子的页面工作正常,并且正确显示 ckeditor 文本区域。但是更新现有帖子的页面显示错误Uncaught ReferenceError: ClassicEditor is not defined
。
我的文件夹结构:
>flask_blog_1
>main
>__init__.py
>routes.py
>posts
>__init__.py
>forms.py
>routes.py
>static
>ckeditor //Custom CKEditor5 Build
>profile_pics
>main.css
>templates
>...
>create_post.html
>layout.html
>...
>users
>__init__.py
>forms.py
>routes.py
>utils.py
>__init__.py
>config.py
>models.py
>instance
>site.db
>venv
>.env
>run.py
下面是我认为是原因的文件的代码。如果需要更多文件的代码,请询问。
posts/routes.py
:
from flask import Blueprint, render_template, flash, redirect, url_for, request, abort
from flaskblog import db
from flaskblog.posts.forms import PostForm
from flaskblog.models import Post
from flask_login import current_user, login_required
posts = Blueprint("posts", __name__)
@posts.route("/post/new", methods=["GET", "POST"])
@login_required
def new_post():
form = PostForm()
if form.validate_on_submit():
post = Post(
title=form.title.data,
content=form.content.data,
author=current_user,
)
db.session.add(post)
db.session.commit()
flash("Your post has been created!", "success")
return redirect(url_for("main.home"))
return render_template(
"create_post.html", title="New Post", form=form, legend="New Post"
)
@posts.route("/post/<int:post_id>")
def post(post_id):
post = Post.query.get_or_404(post_id)
return render_template("post.html", title=post.title, post=post)
@posts.route("/post/<int:post_id>/update", methods=["GET", "POST"])
@login_required
def update_post(post_id):
post = Post.query.get_or_404(post_id)
if post.author != current_user:
abort(403)
form = PostForm()
if form.validate_on_submit():
post.title = form.title.data
post.content = form.content.data
db.session.commit()
flash("Your post has been updated.", "success")
return redirect(url_for("posts.post", post_id=post.id))
elif request.method == "GET":
form.title.data = post.title
form.content.data = post.content
return render_template(
"create_post.html", title="Update Post", form=form, legend="Update Post"
)
@posts.route("/post/<int:post_id>/delete", methods=["POST"])
@login_required
def delete_post(post_id):
post = Post.query.get_or_404(post_id)
if post.author != current_user:
abort(403)
db.session.delete(post)
db.session.commit()
flash("Your post has been deleted.", "success")
return redirect(url_for("main.home"))
new_post
、post
和delete_post
路线运行得很好。问题仅在于 update_post
路线,即使它呈现与 create_post.html
路线相同的 new_post
模板。同样,ckeditor 仅在 Uncaught ReferenceError: ClassicEditor is not defined
路线中显示 update_post
错误。 ckeditor 文本区域也不会显示在此路径中。但在 new_post
路线上效果很好。
templates/layout.html
:
<!DOCTYPE html>
<html>
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"
integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='main.css') }}">
{% if title %}
<title>Flask Blog - {{ title }}</title>
{% else %}
<title>Flask Blog</title>
{% endif %}
<!-- ...................................ckeditor......................................... -->
<!-- <script src="https://cdn.ckeditor.com/ckeditor5/39.0.2/classic/ckeditor.js"></script> -->
<script src="../static/ckeditor/build/ckeditor.js"></script>
<!-- ...................................ckeditor end......................................... -->
</head>
<body>
<header class="site-header">
<nav class="navbar navbar-expand-md navbar-dark bg-steel fixed-top">
<div class="container">
<a class="navbar-brand mr-4" href="/">Flask Blog</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarToggle"
aria-controls="navbarToggle" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarToggle">
<div class="navbar-nav mr-auto">
<a class="nav-item nav-link" href="{{ url_for('main.home') }}">Home</a>
<a class="nav-item nav-link" href="{{ url_for('main.about') }}">About</a>
</div>
<!-- Navbar Right Side -->
<div class="navbar-nav">
{% if current_user.is_authenticated %}
<a class="nav-item nav-link" href="{{ url_for('posts.new_post') }}">New Post</a>
<a class="nav-item nav-link" href="{{ url_for('users.account') }}">{{ current_user.username
}}</a>
<a class="nav-item nav-link" href="{{ url_for('users.logout') }}">Logout</a>
{% else %}
<a class="nav-item nav-link" href="{{ url_for('users.login') }}">Login</a>
<a class="nav-item nav-link" href="{{ url_for('users.register') }}">Register</a>
{% endif %}
</div>
</div>
</div>
</nav>
</header>
<main role="main" class="container">
<div class="row">
<div class="col-md-8">
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div class="alert alert-{{ category }}">
{{ message }}
</div>
{% endfor %}
{% endif %}
{% endwith %}
{% block content %}{% endblock %}
</div>
<div class="col-md-4">
<div class="content-section">
<h3>Sidebar</h3>
<p class='text-muted'>Put some information here.
<ul class="list-group">
<li class="list-group-item list-group-item-light">Latest Posts</li>
<li class="list-group-item list-group-item-light">Announcements</li>
<li class="list-group-item list-group-item-light">Calendars</li>
<li class="list-group-item list-group-item-light">etc</li>
</ul>
</p>
</div>
</div>
</div>
</main>
<!-- Optional JavaScript -->
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"
integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN"
crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"
integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q"
crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"
integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl"
crossorigin="anonymous"></script>
<script>
ClassicEditor
.create(document.querySelector('#content'))
.then(editor => {
editor.model.document.on('change', () => {
document.querySelector('#content-hidden').value = editor.getData();
console.log(editor.getData());
})
})
.catch(error => {
console.error(error);
});
</script>
</body>
</html>
在上面的代码中,在
</head>
标签的正上方,我注释掉了 CDN 的脚本标签,并为自定义 CKEditor 构建编写了脚本标签。当我注释掉第二个脚本标签并取消注释第一个脚本标签时,更新页面工作得很好。
templates/create_post.html
:
{% extends "layout.html" %}
{% block content %}
<div class="content-section">
<form method="POST" action="">
{{ form.hidden_tag() }}
<fieldset class="form-group">
<legend class="border-bottom mb-4">{{ legend }}</legend>
<div class="form-group">
{{ form.title.label(class="form-control-label") }}
{% if form.title.errors %}
{{ form.title(class="form-control form-control-lg is-invalid") }}
<div class="invalid-feedback">
{% for error in form.title.errors %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ form.title(class="form-control form-control-lg") }}
{% endif %}
</div>
<div class="form-group">
{{ form.content.label(class="form-control-label") }}
{% if form.content.errors %}
<!-- {{ form.content(class="form-control form-control-lg is-invalid", id="content") }} -->
<div name="content" id="content" class="form-control form-control-lg is-invalid">
{% if form.content.data %}
{{ form.content.data|safe }}
{% endif %}
</div>
<input type="hidden" name="content" id="content-hidden" />
<div class="invalid-feedback">
{% for error in form.content.errors %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
<!-- {{ form.content(class="form-control form-control-lg", id="content") }} -->
<div name="content" id="content" class="form-control form-control-lg">
{% if form.content.data %}
{{ form.content.data|safe }}
{% endif %}
</div>
<input type="hidden" name="content" id="content-hidden" />
{% endif %}
</div>
</fieldset>
<div class="form-group">
{{ form.submit(class="btn btn-outline-info") }}
</div>
</form>
</div>
{% endblock content %}
这是我第一次尝试使用CKEditor。我不知道为什么它可以与 cdn 一起使用,但不能与自定义构建一起使用。
如果我再次遇到同样的问题,回答这个问题是为了我自己的参考。
所以基本上,当我尝试创建新帖子时,它从正确的位置加载
ckeditor.js
文件,即 flaskblog1/flaskblog/static/ckeditor/build/ckeditor.js
。但是当我尝试更新现有帖子时,它试图从 ckeditor.js
加载 flaskblog1/flaskblog/posts/static/ckeditor/build/ckeditor.js
文件,但显然没有找到。这就是它给出该错误的原因。我仍然不知道为什么要这么做。
我通过使用 Flask 的
url_for
函数解决了这个问题,该函数也可以在模板中使用,无论如何我应该从一开始就这样做。基本上,我将脚本标记更改为以下内容:layout.html
:
<!-- ...................................ckeditor......................................... -->
<script src="{{ url_for('static', filename='ckeditor/build/ckeditor.js') }}"></script>
<!-- ...................................ckeditor end......................................... -->