问题
I'm trying to make turn the Flask Admin text box into a CKEdit box, as described here. However, when I run it and go to the existing admin fields it doesn't show any change to the text boxes, and when I run it and go to the TestAdmin field created to demonstrate I get this error:
OperationalError: (OperationalError) no such table: test u'SELECT count(?) AS count_1 \nFROM test' ('*',)
Along with a bunch of other traceback messages.
I have changed my init script to be this:
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
and I've put the edit.html file in my app/templates
folder.
My best guess at why it's not working is that maybe I'm not supposed to put edit.html
into the templates folder with the other things, but in the flask admin templates folder? But where is that folder? Do I even have one? In edit.html
it says:
{% extends 'admin/model/edit.html' %}
But I don't have that directory. Is that what's screwing it up?
I figure I'm probably doing something pretty stupid due to my lack of understanding of how exactly this thing is trying change the ckeditor template. Can anyone help?
Thanks a lot, Alex
Edit: Here is everything in the edit.html file. I haven't edited the version from github, and I have it in the folder /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 %}
Final Edit: With Mr Cunningham's help, my final version works and looks like this:
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
At first it still wasn't showing text fields as ckedit fields in admin. I got it to work by dropping all the tables and recreating them, and also using db_migrate.py script from Miguel Grinberg's flask tutorial. I also renamed the text field in my models.py
file from Text
to text
, not sure if that had any effect.
回答1:
Here is a simple working example using in-memory SQLite. There are only two files, the flask application and the edit Jinja2 html template.
Library versions used are Flask 0.10.1, Flask-SQLAlchemy 2.1 and Flask-Admin 1.4.
The flask application flask-ckeditor.py
in the root folder:
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)
The Jinja2 html template file 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 %}
回答2:
You can use Flask-CKEditor. It provide a CKEditorField
that is exactly what you need. We need to install it first:
$ pip install flask-ckeditor
Then in your code:
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)
In your template (templates/edit.html):
<!-- Step 4 -->
{% extends 'admin/model/edit.html' %}
{% block tail %}
{{ super() }}
{{ ckeditor.load() }}
{% endblock %}
Get this demo application on GitHub.
来源:https://stackoverflow.com/questions/34971368/getting-ckeditor-to-work-with-flask-admin