flask-admin 算是一個很不錯的 flask 後臺管理了,用它來作博客系統的管理後端再合適不過了,節約時間成本,避免重複造輪子,可是做爲一個程序員,寫文章怎麼能夠沒有 markdown 呢? 如今讓咱們嘗試一下讓 flask-admin 支持 markdown 吧.javascript
這個庫是對 Pagedown 的封裝,將其集成到了Flask-WTF表單中了。具體內容能夠去它的 github 倉庫看一下: Flask-PageDowncss
優勢:html
缺點:java
經過 pip 進行安裝:python
pip install flask-pagedown
而後在項目中初始化:jquery
from flask_pagedown import PageDown app = Flask(__name__) pagedown = PageDown(app)
固然也能夠經過 init_app(app) 的方式進行初始化git
from flask import Flask from flask_pagedown import PageDown pagedown = PageDown() def create_app(config_name): app = Flask(__name__, static_folder='static', static_url_path='') app.config.from_object(config[config_name]) # 省略部分代碼 pagedown.init_app(app) return app
這個編輯器還依賴兩個 js 文件,因此還須要在咱們的對應的模板中添加進去相應的方法:程序員
<html> <head> {{ pagedown.html_head() }} </head> <body> ... </body> </html>
默認狀況下這兩個 js 是經過 CDN 加載的,也就意味着你的服務器須要能夠訪問網絡,若是隻想經過本地就能夠訪問的話,很簡單把這兩個 js 文件下載下來,而後將 pagedown.html_head() 替換成兩個文件的本地路徑就能夠了. 兩個文件的下載地址: Markdown.Converter,
Markdown.Sanitizergithub
flask-admin 支持自定義的 ModelView, 這是咱們能夠實現這個功能的基礎.ajax
class PeachPostView(ModelView): form_overrides = { 'content': PageDownField } def __init__(self, model, session, **kwargs): super(PeachPostView, self).__init__(model, session, **kwargs) # 省略部分代碼
最後在咱們的 flask-admin 給咱們須要 markdown 編輯器的 model 添加上自定義的 ModelView 就能夠了.
# 省略部分代碼 admin.add_view(PeachPostView(Post, db.session, endpoint='AdminPost'))
完整代碼能夠查看 use:使用 flask-pagedown 做爲 markdown 編輯器
最終的效果圖:
沒錯,這個方法就比較自由了,找本身喜歡的 markdown 編輯器而後集成進去. 這裏我最終選擇了 [editor.md]
(https://github.com/pandao/editor.md.git) 這個開源編輯器,這個好像好久沒維護了,可是顏值挺高的了,就決定是它了.
優勢:
缺點:
插件下載很簡單,從官網上或者 github 上下載, 下載完成後,我這裏將 editor.md 下載的文件保存到 static 的 plugin 的目錄下了.
這裏因爲咱們須要使用插件,因此咱們須要對 flask-admin 原有的 create.html 和 edit.html 文件進行重寫.
tip: 上述說所的 flask-admin 的內容,均在你的開發環境中找到 flask-admin 的庫所在目錄,而後須要複製的文件在: flask_admin\templates\bootstrap3\admin 下
而後配置,使其訪問咱們指定的文件
# 自定義的 modelview class PeachPostView(ModelView): # 指定訪問文件 create_template = 'admin/model/peach-post-create.html' edit_template = 'admin/model/peach-post-edit.html' def __init__(self, model, session, **kwargs): super(PeachPostView, self).__init__(model, session, **kwargs) # 指定 base_template 文件 admin = Admin(name=name, template_mode=template_mode,index_view=PeachAdminIndexView(), base_template='admin/peach-base.html') # 省略部分代碼 admin.add_view(PeachPostView(Post, db.session, endpoint='AdminPost'))
動手修改文件的以前,咱們須要先了解一下 flask-amdmin 原有的文件是什麼樣子的,而後才能正確的修改,這裏以 create.html 爲例子,截取關鍵代碼.
# create.html {% import 'admin/lib.html' as lib with context %} # ... {% block edit_form %} {{ lib.render_form(form, return_url, extra(), form_opts) }} # 使用了 lib 中的函數,看一下這個函數 {% endblock %} # lib.html {% macro render_form(form, cancel_url, extra=None, form_opts=None, action=None, is_modal=False) -%} {% call form_tag(action=action) %} {{ render_form_fields(form, form_opts=form_opts) }} # 重點是表單渲染,再看一下 render_form_fields 函數 {{ render_form_buttons(cancel_url, extra, is_modal) }} {% endcall %} {% endmacro %} {% macro render_form_fields(form, form_opts=None) %} # 省略部分代碼 {{ render_field(form, f, kwargs) }} # 看一下 render_field 這個函數 {% endmacro %} {% macro render_field(form, field, kwargs={}, caller=None) %} {% set direct_error = h.is_field_error(field.errors) %} <div class="form-group{{ ' has-error' if direct_error else '' }}"> <label for="{{ field.id }}" class="col-md-2 control-label">{{ field.label.text }} {% if h.is_required_form_field(field) %} <strong style="color: red">*</strong> {%- else -%} {%- endif %} </label> <div class="{{ kwargs.get('column_class', 'col-md-10') }}"> {% set _dummy = kwargs.setdefault('class', 'form-control') %} {{ field(**kwargs)|safe }} {% if field.description %} <p class="help-block">{{ field.description|safe }}</p> {% endif %} {% if direct_error %} <ul class="help-block input-errors"> {% for e in field.errors if e is string %} <li>{{ e }}</li> {% endfor %} </ul> {% endif %} </div> {% if caller %} {{ caller(form, field, direct_error, kwargs) }} {% endif %} </div> {% endmacro %}
經過上述 flask-admin 源碼的瀏覽,咱們不難發現,最終的渲染最核心的函數就是 lib.html 中的 render_field 函數, 要達到咱們的目的,咱們就須要對這個函數進行一點的修改了.因爲咱們只須要對數據庫模型的某個字段支持 markdown 就能夠了,因此須要咱們能夠經過 field.id == '字段名' 的方法去判斷,從而讓他支持 markdown.
爲了更好的理解接下來的代碼,咱們對 editor.md 的使用作一個簡單的說明:
<link rel="stylesheet" href="editormd.min.css" /> <div id="editormd"> <textarea style="display:none;">### Hello Editor.md !</textarea> </div>
<script src="jquery.min.js"></script> <script src="editormd.min.js"></script> <script type="text/javascript"> $(function() { var editor = editormd("editormd", { path : "../lib/" // Autoload modules mode, codemirror, marked... dependents libs path }); /* // or var editor = editormd({ id : "editormd", path : "../lib/" }); */ }); </script>
說明完了怎麼使用 editor.md,咱們來講一下咱們最終的思路, 經過字段名去判斷,而後符合添加的狀況下,新建一個 div, id 爲 editor,而後在 peach-post-create.html 按照 editor.md 的使用方法渲染.說完了思路,接下來就看代碼.
修改 peach-lib.html 和 peach-post-create.html 文件
# peach-lib.html {% macro render_field(form, field, kwargs={}, caller=None) %} {% set direct_error = h.is_field_error(field.errors) %} <div class="form-group{{ ' has-error' if direct_error else '' }}"> <label for="{{ field.id }}" class="col-md-2 control-label">{{ field.label.text }} {% if h.is_required_form_field(field) %} <strong style="color: red">*</strong> {%- else -%} {%- endif %} </label> <div class="{{ kwargs.get('column_class', 'col-md-10') }}"> {% set _dummy = kwargs.setdefault('class', 'form-control') %} {% if field.id == 'content' %} # 符合條件新建一個 div <div id='editormd'> {{ field(**kwargs)|safe }} </div> {% else %} {{ field(**kwargs)|safe }} {% endif %} {% if field.description %} <p class="help-block">{{ field.description|safe }}</p> {% endif %} {% if direct_error %} <ul class="help-block input-errors"> {% for e in field.errors if e is string %} <li>{{ e }}</li> {% endfor %} </ul> {% endif %} </div> {% if caller %} {{ caller(form, field, direct_error, kwargs) }} {% endif %} </div> {% endmacro %} # peach-post-create.html {% extends 'admin/master.html' %} {% import 'admin/peach-lib.html' as lib with context %} {% from 'admin/peach-lib.html' import extra with context %} {# backward compatible #} # 省略部分代碼 {{ super() }} {{ lib.form_css() }} <link href="{{ url_for('static', filename='plugins/editor.md/css/editormd.min.css')}}" rel='stylesheet'> {% endblock %} # 省略部分代碼 {% block tail %} {{ super() }} {{ lib.form_js() }} <script src="{{ url_for('static', filename='plugins/editor.md/editormd.js')}}"></script> <script type="text/javascript"> $(function() { var editor = editormd("editormd", { path : "/plugins/editor.md/lib/", height : 640 }); }); </script> {% endblock %}
修改 modelview
class PeachPostView(ModelView): # 省略部分代碼 create_template = 'admin/model/peach-post-create.html' admin = Admin(name=name, template_mode=template_mode,index_view=PeachAdminIndexView(), base_template='admin/peach-base.html') # 省略部分代碼 admin.add_view(PeachPostView(Post, db.session, endpoint='AdminPost'))
完整代碼能夠查看 add:使用 editor.md 編輯器
最後看一下效果圖:
到此大功告成,歡迎你們關注個人公共號-Leetao, 偶爾分享 flask 以外的知識.