让 CKEditor 与 Flask Admin 一起工作
Getting CKEditor to work with Flask Admin
我正在尝试将 Flask Admin 文本框变成 CKEdit 框,如此处所述。但是,当我 运行 它并转到现有的管理字段时,它没有显示对文本框的任何更改,当我 运行 它并转到创建的 TestAdmin 字段以演示我得到这个错误:
OperationalError: (OperationalError) no such table: test u'SELECT count(?) AS count_1 \nFROM test' ('*',)
连同一堆其他回溯消息。
我已将初始化脚本更改为:
import os
from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
from flask.ext.login import LoginManager
from flask.ext.openid import OpenID
from flask.ext.mail import Mail
from config import basedir, ADMINS, MAIL_SERVER, MAIL_PORT, MAIL_USERNAME, MAIL_PASSWORD
from momentjs import momentjs
from flask.ext.babel import Babel
from flask.ext import admin
from flask_admin.contrib.sqla import ModelView
from wtforms import TextAreaField
from wtforms.widgets import TextArea
app = Flask(__name__)
app.config.from_object('config')
db = SQLAlchemy(app)
lm = LoginManager()
lm.init_app(app)
lm.login_view = 'login'
oid = OpenID(app, os.path.join(basedir, 'tmp'))
mail = Mail(app)
babel = Babel(app)
from app import views, models
###
class CKTextAreaWidget(TextArea):
def __call__(self, field, **kwargs):
if kwargs.get('class'):
kwargs['class'] += " ckeditor"
else:
kwargs.setdefault('class', 'ckeditor')
return super(CKTextAreaWidget, self).__call__(field, **kwargs)
class CKTextAreaField(TextAreaField):
widget = CKTextAreaWidget()
class Test(db.Model):
id = db.Column(db.Integer, primary_key=True)
text = db.Column(db.UnicodeText)
class TestAdmin(ModelView):
form_overrides = dict(text=CKTextAreaField)
create_template = 'edit.html'
edit_template = 'edit.html'
admin = admin.Admin(app, name = 'PetroTools', template_mode = 'bootstrap3')
admin.add_view(ModelView(models.Report, db.session))
admin.add_view(ModelView(models.Well, db.session))
admin.add_view(ModelView(models.Field, db.session))
admin.add_view(ModelView(models.Section, db.session))
admin.add_view(TestAdmin(Test, db.session))
if not app.debug:
import logging
from logging.handlers import SMTPHandler
credentials = None
if MAIL_USERNAME or MAIL_PASSWORD:
credentials = (MAIL_USERNAME, MAIL_PASSWORD)
mail_handler = SMTPHandler((MAIL_SERVER, MAIL_PORT), 'no-reply@' + MAIL_SERVER, ADMINS, 'microblog failure', credentials)
mail_handler.setLevel(logging.ERROR)
app.logger.addHandler(mail_handler)
if not app.debug:
import logging
from logging.handlers import RotatingFileHandler
file_handler = RotatingFileHandler('tmp/microblog.log', 'a', 1 * 1024 * 1024, 10)
file_handler.setFormatter(logging.Formatter('%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]'))
app.logger.setLevel(logging.INFO)
file_handler.setLevel(logging.INFO)
app.logger.addHandler(file_handler)
app.logger.info('microblog startup')
app.jinja_env.globals['momentjs'] = momentjs
并且我已将 edit.html 文件放入我的 app/templates
文件夹中。
我对它为什么不起作用的最佳猜测是,也许我不应该将 edit.html
与其他东西一起放入模板文件夹中,而是放在 flask 管理模板文件夹中?但是那个文件夹在哪里?我有一个吗?在 edit.html
它说:
{% extends 'admin/model/edit.html' %}
但是我没有那个目录。这就是搞砸的原因吗?
我想我可能正在做一些非常愚蠢的事情,因为我不了解这件事究竟是如何尝试改变 ckeditor 模板的。有人可以帮忙吗?
非常感谢,
亚历克斯
编辑: 这是 edit.html 文件中的所有内容。我没有编辑 github 的版本,我在文件夹 /app/templates/
.
中有它
{% extends 'admin/model/edit.html' %}
{% block tail %}
{{ super() }}
<script src="http://cdnjs.cloudflare.com/ajax/libs/ckeditor/4.0.1/ckeditor.js"></script>
{% endblock %}
最终编辑:
在 Cunningham 先生的帮助下,我的最终版本是这样的:
import os
from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
from flask.ext.login import LoginManager
from flask.ext.openid import OpenID
from flask.ext.mail import Mail
from config import basedir, ADMINS, MAIL_SERVER, MAIL_PORT, MAIL_USERNAME, MAIL_PASSWORD
from momentjs import momentjs
from flask.ext.babel import Babel
###
from flask.ext import admin
from flask_admin.contrib.sqla import ModelView
from wtforms import TextAreaField
from wtforms.widgets import TextArea
###
app = Flask(__name__)
app.config.from_object('config')
db = SQLAlchemy(app)
lm = LoginManager()
lm.init_app(app)
lm.login_view = 'login'
oid = OpenID(app, os.path.join(basedir, 'tmp'))
mail = Mail(app)
babel = Babel(app)
from app import views, models
###
class CKTextAreaWidget(TextArea):
def __call__(self, field, **kwargs):
if kwargs.get('class'):
kwargs['class'] += " ckeditor"
else:
kwargs.setdefault('class', 'ckeditor')
return super(CKTextAreaWidget, self).__call__(field, **kwargs)
class CKTextAreaField(TextAreaField):
widget = CKTextAreaWidget()
class TestAdmin(ModelView):
form_overrides = dict(text=CKTextAreaField)
create_template = 'edit.html'
edit_template = 'edit.html'
###ADMIN###
admin = admin.Admin(app, name = 'PetroTools', template_mode = 'bootstrap3')
admin.add_view(TestAdmin(models.Report, db.session))
admin.add_view(TestAdmin(models.Well, db.session))
admin.add_view(TestAdmin(models.Field, db.session))
admin.add_view(TestAdmin(models.Section, db.session))
###########
if not app.debug:
import logging
from logging.handlers import SMTPHandler
credentials = None
if MAIL_USERNAME or MAIL_PASSWORD:
credentials = (MAIL_USERNAME, MAIL_PASSWORD)
mail_handler = SMTPHandler((MAIL_SERVER, MAIL_PORT), 'no-reply@' + MAIL_SERVER, ADMINS, 'microblog failure', credentials)
mail_handler.setLevel(logging.ERROR)
app.logger.addHandler(mail_handler)
if not app.debug:
import logging
from logging.handlers import RotatingFileHandler
file_handler = RotatingFileHandler('tmp/microblog.log', 'a', 1 * 1024 * 1024, 10)
file_handler.setFormatter(logging.Formatter('%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]'))
app.logger.setLevel(logging.INFO)
file_handler.setLevel(logging.INFO)
app.logger.addHandler(file_handler)
app.logger.info('microblog startup')
app.jinja_env.globals['momentjs'] = momentjs
起初它仍然没有在管理中将文本字段显示为 ckedit 字段。我通过删除所有表并重新创建它们以及使用 Miguel Grinberg 的烧瓶教程中的 db_migrate.py 脚本来让它工作。我还将 models.py
文件中的文本字段从 Text
重命名为 text
,不确定是否有任何影响。
这是一个使用 in-memory SQLite 的简单工作示例。只有两个文件,flask 应用程序和编辑 Jinja2 html 模板。
使用的库版本是 Flask 0.10.1、Flask-SQLAlchemy 2.1 和 Flask-Admin 1.4。
根文件夹中的烧瓶应用程序flask-ckeditor.py
:
from flask import Flask
from flask.ext.admin import Admin
from flask.ext.admin.contrib.sqla import ModelView
from flask.ext.admin.menu import MenuLink
from flask.ext.sqlalchemy import SQLAlchemy
from wtforms.widgets import TextArea, TextInput
from wtforms.fields import TextAreaField
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///:memory:'
app.config['SQLALCHEMY_ECHO'] = True
app.config['DEBUG'] = True
app.config['SECRET_KEY'] = 'super-secret'
db = SQLAlchemy(app)
class Test(db.Model):
__tablename__ = 'tests'
id = db.Column(db.Integer, primary_key=True)
text = db.Column(db.UnicodeText)
class CKEditorWidget(TextArea):
def __call__(self, field, **kwargs):
if kwargs.get('class'):
kwargs['class'] += " ckeditor"
else:
kwargs.setdefault('class', 'ckeditor')
return super(CKEditorWidget, self).__call__(field, **kwargs)
class CKEditorField(TextAreaField):
widget = CKEditorWidget()
class TestAdminView(ModelView):
form_overrides = dict(text=CKEditorField)
can_view_details = True
create_template = 'edit.html'
edit_template = 'edit.html'
@app.route('/')
def index():
return '<a href="/admin/">Click me to get to Admin!</a>'
# Create admin
admin = Admin(app, name='Admin')
admin.add_view(TestAdminView(model=Test, session=db.session, category='Tables', name='Test'))
admin.add_link(MenuLink(name='Public Website', category='', url='/'))
def build_db():
tests = [
{
'text': "<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut</p>"
},
{
'text': "<p>Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque<p>"
},
{
'text': "<p>At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium</p>"
}
]
db.drop_all()
db.create_all()
for test in tests:
test_db = Test(**test)
db.session.add(test_db)
db.session.commit()
@app.before_first_request
def create_user():
build_db()
if __name__ == '__main__':
app.run(debug=True)
Jinja2 html模板文件templates/edit.html
:
{% extends 'admin/model/edit.html' %}
{% block tail %}
{{ super() }}
<script src="http://cdnjs.cloudflare.com/ajax/libs/ckeditor/4.0.1/ckeditor.js"></script>
{% endblock %}
您可以使用 Flask-CKEditor。它提供的 CKEditorField
正是您所需要的。我们需要先安装它:
$ pip install flask-ckeditor
然后在你的代码中:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_admin import Admin
from flask_admin.contrib.sqla import ModelView
from flask_ckeditor import CKEditor, CKEditorField # Step 1
app = Flask(__name__)
app.config['SECRET_KEY'] = 'dev'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///'
db = SQLAlchemy(app)
ckeditor = CKEditor(app) # Step 2
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(120))
text = db.Column(db.Text)
# Step 3
class PostAdmin(ModelView):
form_overrides = dict(text=CKEditorField)
create_template = 'edit.html'
edit_template = 'edit.html'
admin = Admin(app, name='Flask-CKEditor demo')
admin.add_view(PostAdmin(Post, db.session))
if __name__ == '__main__':
app.run(debug=True)
在您的模板中 (templates/edit.html):
<!-- Step 4 -->
{% extends 'admin/model/edit.html' %}
{% block tail %}
{{ super() }}
{{ ckeditor.load() }}
{#
If you have set the configuration variables more than CKEDITOR_SERVE_LOCAL and CKEDITOR_PKG_TYPE,
or you need to config the CKEditor textarea, use the line below to register the configuration.
The name value should be the name of the CKEditor form field.
#}
{{ ckeditor.config(name='text') }}
{% endblock %}
在 GitHub 上获取此演示应用程序。
我正在尝试将 Flask Admin 文本框变成 CKEdit 框,如此处所述。但是,当我 运行 它并转到现有的管理字段时,它没有显示对文本框的任何更改,当我 运行 它并转到创建的 TestAdmin 字段以演示我得到这个错误:
OperationalError: (OperationalError) no such table: test u'SELECT count(?) AS count_1 \nFROM test' ('*',)
连同一堆其他回溯消息。
我已将初始化脚本更改为:
import os
from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
from flask.ext.login import LoginManager
from flask.ext.openid import OpenID
from flask.ext.mail import Mail
from config import basedir, ADMINS, MAIL_SERVER, MAIL_PORT, MAIL_USERNAME, MAIL_PASSWORD
from momentjs import momentjs
from flask.ext.babel import Babel
from flask.ext import admin
from flask_admin.contrib.sqla import ModelView
from wtforms import TextAreaField
from wtforms.widgets import TextArea
app = Flask(__name__)
app.config.from_object('config')
db = SQLAlchemy(app)
lm = LoginManager()
lm.init_app(app)
lm.login_view = 'login'
oid = OpenID(app, os.path.join(basedir, 'tmp'))
mail = Mail(app)
babel = Babel(app)
from app import views, models
###
class CKTextAreaWidget(TextArea):
def __call__(self, field, **kwargs):
if kwargs.get('class'):
kwargs['class'] += " ckeditor"
else:
kwargs.setdefault('class', 'ckeditor')
return super(CKTextAreaWidget, self).__call__(field, **kwargs)
class CKTextAreaField(TextAreaField):
widget = CKTextAreaWidget()
class Test(db.Model):
id = db.Column(db.Integer, primary_key=True)
text = db.Column(db.UnicodeText)
class TestAdmin(ModelView):
form_overrides = dict(text=CKTextAreaField)
create_template = 'edit.html'
edit_template = 'edit.html'
admin = admin.Admin(app, name = 'PetroTools', template_mode = 'bootstrap3')
admin.add_view(ModelView(models.Report, db.session))
admin.add_view(ModelView(models.Well, db.session))
admin.add_view(ModelView(models.Field, db.session))
admin.add_view(ModelView(models.Section, db.session))
admin.add_view(TestAdmin(Test, db.session))
if not app.debug:
import logging
from logging.handlers import SMTPHandler
credentials = None
if MAIL_USERNAME or MAIL_PASSWORD:
credentials = (MAIL_USERNAME, MAIL_PASSWORD)
mail_handler = SMTPHandler((MAIL_SERVER, MAIL_PORT), 'no-reply@' + MAIL_SERVER, ADMINS, 'microblog failure', credentials)
mail_handler.setLevel(logging.ERROR)
app.logger.addHandler(mail_handler)
if not app.debug:
import logging
from logging.handlers import RotatingFileHandler
file_handler = RotatingFileHandler('tmp/microblog.log', 'a', 1 * 1024 * 1024, 10)
file_handler.setFormatter(logging.Formatter('%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]'))
app.logger.setLevel(logging.INFO)
file_handler.setLevel(logging.INFO)
app.logger.addHandler(file_handler)
app.logger.info('microblog startup')
app.jinja_env.globals['momentjs'] = momentjs
并且我已将 edit.html 文件放入我的 app/templates
文件夹中。
我对它为什么不起作用的最佳猜测是,也许我不应该将 edit.html
与其他东西一起放入模板文件夹中,而是放在 flask 管理模板文件夹中?但是那个文件夹在哪里?我有一个吗?在 edit.html
它说:
{% extends 'admin/model/edit.html' %}
但是我没有那个目录。这就是搞砸的原因吗?
我想我可能正在做一些非常愚蠢的事情,因为我不了解这件事究竟是如何尝试改变 ckeditor 模板的。有人可以帮忙吗?
非常感谢, 亚历克斯
编辑: 这是 edit.html 文件中的所有内容。我没有编辑 github 的版本,我在文件夹 /app/templates/
.
{% extends 'admin/model/edit.html' %}
{% block tail %}
{{ super() }}
<script src="http://cdnjs.cloudflare.com/ajax/libs/ckeditor/4.0.1/ckeditor.js"></script>
{% endblock %}
最终编辑: 在 Cunningham 先生的帮助下,我的最终版本是这样的:
import os
from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
from flask.ext.login import LoginManager
from flask.ext.openid import OpenID
from flask.ext.mail import Mail
from config import basedir, ADMINS, MAIL_SERVER, MAIL_PORT, MAIL_USERNAME, MAIL_PASSWORD
from momentjs import momentjs
from flask.ext.babel import Babel
###
from flask.ext import admin
from flask_admin.contrib.sqla import ModelView
from wtforms import TextAreaField
from wtforms.widgets import TextArea
###
app = Flask(__name__)
app.config.from_object('config')
db = SQLAlchemy(app)
lm = LoginManager()
lm.init_app(app)
lm.login_view = 'login'
oid = OpenID(app, os.path.join(basedir, 'tmp'))
mail = Mail(app)
babel = Babel(app)
from app import views, models
###
class CKTextAreaWidget(TextArea):
def __call__(self, field, **kwargs):
if kwargs.get('class'):
kwargs['class'] += " ckeditor"
else:
kwargs.setdefault('class', 'ckeditor')
return super(CKTextAreaWidget, self).__call__(field, **kwargs)
class CKTextAreaField(TextAreaField):
widget = CKTextAreaWidget()
class TestAdmin(ModelView):
form_overrides = dict(text=CKTextAreaField)
create_template = 'edit.html'
edit_template = 'edit.html'
###ADMIN###
admin = admin.Admin(app, name = 'PetroTools', template_mode = 'bootstrap3')
admin.add_view(TestAdmin(models.Report, db.session))
admin.add_view(TestAdmin(models.Well, db.session))
admin.add_view(TestAdmin(models.Field, db.session))
admin.add_view(TestAdmin(models.Section, db.session))
###########
if not app.debug:
import logging
from logging.handlers import SMTPHandler
credentials = None
if MAIL_USERNAME or MAIL_PASSWORD:
credentials = (MAIL_USERNAME, MAIL_PASSWORD)
mail_handler = SMTPHandler((MAIL_SERVER, MAIL_PORT), 'no-reply@' + MAIL_SERVER, ADMINS, 'microblog failure', credentials)
mail_handler.setLevel(logging.ERROR)
app.logger.addHandler(mail_handler)
if not app.debug:
import logging
from logging.handlers import RotatingFileHandler
file_handler = RotatingFileHandler('tmp/microblog.log', 'a', 1 * 1024 * 1024, 10)
file_handler.setFormatter(logging.Formatter('%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]'))
app.logger.setLevel(logging.INFO)
file_handler.setLevel(logging.INFO)
app.logger.addHandler(file_handler)
app.logger.info('microblog startup')
app.jinja_env.globals['momentjs'] = momentjs
起初它仍然没有在管理中将文本字段显示为 ckedit 字段。我通过删除所有表并重新创建它们以及使用 Miguel Grinberg 的烧瓶教程中的 db_migrate.py 脚本来让它工作。我还将 models.py
文件中的文本字段从 Text
重命名为 text
,不确定是否有任何影响。
这是一个使用 in-memory SQLite 的简单工作示例。只有两个文件,flask 应用程序和编辑 Jinja2 html 模板。
使用的库版本是 Flask 0.10.1、Flask-SQLAlchemy 2.1 和 Flask-Admin 1.4。
根文件夹中的烧瓶应用程序flask-ckeditor.py
:
from flask import Flask
from flask.ext.admin import Admin
from flask.ext.admin.contrib.sqla import ModelView
from flask.ext.admin.menu import MenuLink
from flask.ext.sqlalchemy import SQLAlchemy
from wtforms.widgets import TextArea, TextInput
from wtforms.fields import TextAreaField
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///:memory:'
app.config['SQLALCHEMY_ECHO'] = True
app.config['DEBUG'] = True
app.config['SECRET_KEY'] = 'super-secret'
db = SQLAlchemy(app)
class Test(db.Model):
__tablename__ = 'tests'
id = db.Column(db.Integer, primary_key=True)
text = db.Column(db.UnicodeText)
class CKEditorWidget(TextArea):
def __call__(self, field, **kwargs):
if kwargs.get('class'):
kwargs['class'] += " ckeditor"
else:
kwargs.setdefault('class', 'ckeditor')
return super(CKEditorWidget, self).__call__(field, **kwargs)
class CKEditorField(TextAreaField):
widget = CKEditorWidget()
class TestAdminView(ModelView):
form_overrides = dict(text=CKEditorField)
can_view_details = True
create_template = 'edit.html'
edit_template = 'edit.html'
@app.route('/')
def index():
return '<a href="/admin/">Click me to get to Admin!</a>'
# Create admin
admin = Admin(app, name='Admin')
admin.add_view(TestAdminView(model=Test, session=db.session, category='Tables', name='Test'))
admin.add_link(MenuLink(name='Public Website', category='', url='/'))
def build_db():
tests = [
{
'text': "<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut</p>"
},
{
'text': "<p>Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque<p>"
},
{
'text': "<p>At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium</p>"
}
]
db.drop_all()
db.create_all()
for test in tests:
test_db = Test(**test)
db.session.add(test_db)
db.session.commit()
@app.before_first_request
def create_user():
build_db()
if __name__ == '__main__':
app.run(debug=True)
Jinja2 html模板文件templates/edit.html
:
{% extends 'admin/model/edit.html' %}
{% block tail %}
{{ super() }}
<script src="http://cdnjs.cloudflare.com/ajax/libs/ckeditor/4.0.1/ckeditor.js"></script>
{% endblock %}
您可以使用 Flask-CKEditor。它提供的 CKEditorField
正是您所需要的。我们需要先安装它:
$ pip install flask-ckeditor
然后在你的代码中:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_admin import Admin
from flask_admin.contrib.sqla import ModelView
from flask_ckeditor import CKEditor, CKEditorField # Step 1
app = Flask(__name__)
app.config['SECRET_KEY'] = 'dev'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///'
db = SQLAlchemy(app)
ckeditor = CKEditor(app) # Step 2
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(120))
text = db.Column(db.Text)
# Step 3
class PostAdmin(ModelView):
form_overrides = dict(text=CKEditorField)
create_template = 'edit.html'
edit_template = 'edit.html'
admin = Admin(app, name='Flask-CKEditor demo')
admin.add_view(PostAdmin(Post, db.session))
if __name__ == '__main__':
app.run(debug=True)
在您的模板中 (templates/edit.html):
<!-- Step 4 -->
{% extends 'admin/model/edit.html' %}
{% block tail %}
{{ super() }}
{{ ckeditor.load() }}
{#
If you have set the configuration variables more than CKEDITOR_SERVE_LOCAL and CKEDITOR_PKG_TYPE,
or you need to config the CKEditor textarea, use the line below to register the configuration.
The name value should be the name of the CKEditor form field.
#}
{{ ckeditor.config(name='text') }}
{% endblock %}
在 GitHub 上获取此演示应用程序。