最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Flask的圖形化管理界面搭建框架Flask-Admin的使用教程

 更新時間:2016年06月13日 15:57:10   作者:digwtx  
Flask-Admin是一個為Python的Flask框架服務的微型框架,可以像Django-Admin那樣為用戶生成Model層面的數(shù)據(jù)管理界面,接下來就一起來看一下Flask的圖形化管理界面搭建框架Flask-Admin的使用教程

Flask-Admin是Flask框架的一個擴展,用它能夠快速創(chuàng)建Web管理界面,它實現(xiàn)了比如用戶、文件的增刪改查等常用的管理功能;如果對它的默認界面不喜歡,可以通過修改模板文件來定制;
Flask-Admin把每一個菜單(超鏈接)看作一個view,注冊后才能顯示出來,view本身也有屬性來控制其是否可見;因此,利用這個機制可以定制自己的模塊化界面,比如讓不同權(quán)限的用戶登錄后看到不一樣的菜單;

項目地址:https://flask-admin.readthedocs.io/en/latest/

example/simple
這是最簡單的一個樣例,可以幫助我們快速、直觀的了解基本概念,學會定制Flask-Admin的界面
simple.py:

from flask import Flask

from flask.ext import admin


# Create custom admin view
class MyAdminView(admin.BaseView):
  @admin.expose('/')
  def index(self):
    return self.render('myadmin.html')


class AnotherAdminView(admin.BaseView):
  @admin.expose('/')
  def index(self):
    return self.render('anotheradmin.html')

  @admin.expose('/test/')
  def test(self):
    return self.render('test.html')


# Create flask app
app = Flask(__name__, template_folder='templates')
app.debug = True

# Flask views
@app.route('/')
def index():
  return '<a href="/admin/">Click me to get to Admin!</a>'

# Create admin interface
admin = admin.Admin()
admin.add_view(MyAdminView(category='Test'))
admin.add_view(AnotherAdminView(category='Test'))
admin.init_app(app)

if __name__ == '__main__':

  # Start app
  app.run()

在這里可以看到運行效果

BaseView

所有的view都必須繼承自BaseView:

復制代碼 代碼如下:

class BaseView(name=None, category=None, endpoint=None, url=None, static_folder=None, static_url_path=None)


name: view在頁面上表現(xiàn)為一個menu(超鏈接),menu name == 'name',缺省就用小寫的class name
category: 如果多個view有相同的category就全部放到一個dropdown里面(dropdown name=='category')
endpoint: 假設endpoint='xxx',則可以用url_for(xxx.index),也能改變頁面URL(/admin/xxx)
url: 頁面URL,優(yōu)先級url > endpoint > class name
static_folder: static目錄的路徑
static_url_path: static目錄的URL
anotheradmin.html:

{% extends 'admin/master.html' %}
{% block body %}
  Hello World from AnotherMyAdmin!<br/>
  <a href="{{ url_for('.test') }}">Click me to go to test view</a>
{% endblock %}

如果AnotherAdminView增加參數(shù)endpoint='xxx',那這里就可以寫成url_for('xxx.text'),然后頁面URL會由/admin/anotheradminview/變成/admin/xxx
如果同時指定參數(shù)url='aaa',那頁面URL會變成/admin/aaa,url優(yōu)先級比endpoint高
Admin

復制代碼 代碼如下:

class Admin(app=None, name=None, url=None, subdomain=None, index_view=None, translations_path=None, endpoint=None, static_url_path=None, base_template=None)

app: Flask Application Object;本例中可以不寫admin.init_app(app),直接用admin = admin.Admin(app=app)是一樣的
name: Application name,缺省'Admin';會顯示為main menu name('Home'左邊的'Admin')和page title
subdomain: ???
index_view: 'Home'那個menu對應的就叫index view,缺省AdminIndexView
base_template: 基礎模板,缺省admin/base.html,該模板在Flask-Admin的源碼目錄里面
部分Admin代碼如下:
class MenuItem(object):
  """
    Simple menu tree hierarchy.
  """
  def __init__(self, name, view=None):
    self.name = name
    self._view = view
    self._children = []
    self._children_urls = set()
    self._cached_url = None
    self.url = None
    if view is not None:
      self.url = view.url

  def add_child(self, view):
    self._children.append(view)
    self._children_urls.add(view.url)

class Admin(object):

  def __init__(self, app=None, name=None,
         url=None, subdomain=None,
         index_view=None,
         translations_path=None,
         endpoint=None,
         static_url_path=None,
         base_template=None):

    self.app = app

    self.translations_path = translations_path

    self._views = []
    self._menu = []
    self._menu_categories = dict()
    self._menu_links = []

    if name is None:
      name = 'Admin'
    self.name = name

    self.index_view = index_view or AdminIndexView(endpoint=endpoint, url=url)
    self.endpoint = endpoint or self.index_view.endpoint
    self.url = url or self.index_view.url
    self.static_url_path = static_url_path
    self.subdomain = subdomain
    self.base_template = base_template or 'admin/base.html'

    # Add predefined index view
    self.add_view(self.index_view)

    # Register with application
    if app is not None:
      self._init_extension()

  def add_view(self, view):

    # Add to views
    self._views.append(view)

    # If app was provided in constructor, register view with Flask app
    if self.app is not None:
      self.app.register_blueprint(view.create_blueprint(self))
      self._add_view_to_menu(view)

  def _add_view_to_menu(self, view):

    if view.category:
      category = self._menu_categories.get(view.category)

      if category is None:
        category = MenuItem(view.category)
        self._menu_categories[view.category] = category
        self._menu.append(category)

      category.add_child(MenuItem(view.name, view))
    else:
      self._menu.append(MenuItem(view.name, view))

  def init_app(self, app):

    self.app = app

    self._init_extension()

    # Register views
    for view in self._views:
      app.register_blueprint(view.create_blueprint(self))
      self._add_view_to_menu(view)

從上面的代碼可以看出init_app(app)和Admin(app=app)是一樣的:
將每個view注冊為blueprint(Flask里的概念,可以簡單理解為模塊)
記錄所有view,以及所屬的category和url
AdminIndexView

復制代碼 代碼如下:

class AdminIndexView(name=None, category=None, endpoint=None, url=None, template='admin/index.html')


name: 缺省'Home'
endpoint: 缺省'admin'
url: 缺省'/admin'
如果要封裝出自己的view,可以參照AdminIndexView的寫法:

class AdminIndexView(BaseView):

  def __init__(self, name=None, category=None,
         endpoint=None, url=None,
         template='admin/index.html'):
    super(AdminIndexView, self).__init__(name or babel.lazy_gettext('Home'),
                       category,
                       endpoint or 'admin',
                       url or '/admin',
                       'static')
    self._template = template

  @expose()
  def index(self):
    return self.render(self._template)
base_template

base_template缺省是/admin/base.html,是頁面的主要代碼(基于bootstrap),它里面又import admin/layout.html;
layout是一些宏,主要用于展開、顯示menu;
在模板中使用一些變量來取出之前注冊view時保存的信息(如menu name和url等):
# admin/layout.html (部分)

{% macro menu() %}
 {% for item in admin_view.admin.menu() %}
  {% if item.is_category() %}
   {% set children = item.get_children() %}
   {% if children %}
    {% if item.is_active(admin_view) %}<li class="active dropdown">{% else %}<li class="dropdown">{% endif %}
     <a class="dropdown-toggle" data-toggle="dropdown" href="javascript:void(0)">{{ item.name }}<b class="caret"></b></a>
     <ul class="dropdown-menu">
      {% for child in children %}
       {% if child.is_active(admin_view) %}<li class="active">{% else %}<li>{% endif %}
        <a href="{{ child.get_url() }}">{{ child.name }}</a>
       </li>
      {% endfor %}
     </ul>
    </li>
   {% endif %}
  {% else %}
   {% if item.is_accessible() and item.is_visible() %}
    {% if item.is_active(admin_view) %}<li class="active">{% else %}<li>{% endif %}
     <a href="{{ item.get_url() }}">{{ item.name }}</a>
    </li>
   {% endif %}
  {% endif %}
 {% endfor %}
{% endmacro %}

example/file
這個樣例能幫助我們快速搭建起文件管理界面,但我們的重點是學習使用ActionsMixin模塊
file.py:

import os
import os.path as op

from flask import Flask

from flask.ext import admin
from flask.ext.admin.contrib import fileadmin

# Create flask app
app = Flask(__name__, template_folder='templates', static_folder='files')

# Create dummy secrey key so we can use flash
app.config['SECRET_KEY'] = '123456790'


# Flask views
@app.route('/')
def index():
  return '<a href="/admin/">Click me to get to Admin!</a>'


if __name__ == '__main__':
  # Create directory
  path = op.join(op.dirname(__file__), 'files')
  try:
    os.mkdir(path)
  except OSError:
    pass

  # Create admin interface
  admin = admin.Admin(app)
  admin.add_view(fileadmin.FileAdmin(path, '/files/', name='Files'))

  # Start app
  app.run(debug=True)

FileAdmin是已經(jīng)寫好的的一個view,直接用即可:

復制代碼 代碼如下:

class FileAdmin(base_path, base_url, name=None, category=None, endpoint=None, url=None, verify_path=True)


base_path: 文件存放的相對路徑
base_url: 文件目錄的URL
FileAdmin中和ActionsMixin相關(guān)代碼如下:
class FileAdmin(BaseView, ActionsMixin):

  def __init__(self, base_path, base_url,
         name=None, category=None, endpoint=None, url=None,
         verify_path=True):

    self.init_actions()

@expose('/action/', methods=('POST',))
def action_view(self):
  return self.handle_action()

# Actions
@action('delete',
    lazy_gettext('Delete'),
    lazy_gettext('Are you sure you want to delete these files?'))
def action_delete(self, items):
  if not self.can_delete:
    flash(gettext('File deletion is disabled.'), 'error')
    return

  for path in items:
    base_path, full_path, path = self._normalize_path(path)

    if self.is_accessible_path(path):
      try:
        os.remove(full_path)
        flash(gettext('File "%(name)s" was successfully deleted.', name=path))
      except Exception as ex:
        flash(gettext('Failed to delete file: %(name)s', name=ex), 'error')

@action('edit', lazy_gettext('Edit'))
def action_edit(self, items):
  return redirect(url_for('.edit', path=items))
@action()用于wrap跟在后面的函數(shù),這里的作用就是把參數(shù)保存起來:
def action(name, text, confirmation=None)
  def wrap(f):
    f._action = (name, text, confirmation)
    return f

  return wrap

name: action name
text: 可用于按鈕名稱
confirmation: 彈框確認信息
init_actions()把所有action的信息保存到ActionsMixin里面:

# 調(diào)試信息
_actions = [('delete', lu'Delete'), ('edit', lu'Edit')]
_actions_data = {'edit': (<bound method FileAdmin.action_edit of <flask_admin.contrib.fileadmin.FileAdmin object at 0x1aafc50>>, lu'Edit', None), 'delete': (<bound method FileAdmin.action_delete of <flask_admin.contrib.fileadmin.FileAdmin object at 0x1aafc50>>, lu'Delete', lu'Are you sure you want to delete these files?')}

action_view()用于處理POST給/action/的請求,然后調(diào)用handle_action(),它再調(diào)用不同的action處理,最后返回當前頁面:

# 省略無關(guān)代碼
def handle_action(self, return_view=None):

  action = request.form.get('action')
  ids = request.form.getlist('rowid')

  handler = self._actions_data.get(action)

  if handler and self.is_action_allowed(action):
    response = handler[0](ids)

    if response is not None:
      return response

  if not return_view:
    url = url_for('.' + self._default_view)
  else:
    url = url_for('.' + return_view)

  return redirect(url)

ids是一個文件清單,作為參數(shù)傳給action處理函數(shù)(參數(shù)items):

# 調(diào)試信息
ids: [u'1.png', u'2.png']

再分析頁面代碼,F(xiàn)iles頁面對應文件為admin/file/list.html,重點看With selected下拉菜單相關(guān)代碼:
{% import 'admin/actions.html' as actionslib with context %}

{% if actions %}
  <div class="btn-group">
    {{ actionslib.dropdown(actions, 'dropdown-toggle btn btn-large') }}
  </div>
{% endif %}

{% block actions %}
  {{ actionslib.form(actions, url_for('.action_view')) }}
{% endblock %}

{% block tail %}
  {{ actionslib.script(_gettext('Please select at least one file.'),
           actions,
           actions_confirmation) }}
{% endblock %}

上面用到的三個宏在actions.html:

{% macro dropdown(actions, btn_class='dropdown-toggle') -%}
  <a class="{{ btn_class }}" data-toggle="dropdown" href="javascript:void(0)">{{ _gettext('With selected') }}<b class="caret"></b></a>
  <ul class="dropdown-menu">
    {% for p in actions %}
    <li>
      <a href="javascript:void(0)" onclick="return modelActions.execute('{{ p[0] }}');">{{ _gettext(p[1]) }}</a>
    </li>
    {% endfor %}
  </ul>
{% endmacro %}

{% macro form(actions, url) %}
  {% if actions %}
  <form id="action_form" action="{{ url }}" method="POST" style="display: none">
    {% if csrf_token %}
    <input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
    {% endif %}
    <input type="hidden" id="action" name="action" />
  </form>
  {% endif %}
{% endmacro %}

{% macro script(message, actions, actions_confirmation) %}
  {% if actions %}
  <script src="{{ admin_static.url(filename='admin/js/actions.js') }}"></script>
  <script language="javascript">
    var modelActions = new AdminModelActions({{ message|tojson|safe }}, {{ actions_confirmation|tojson|safe }});
  </script>
  {% endif %}
{% endmacro %}

最終生成的頁面(部分):

<div class="btn-group">
  <a class="dropdown-toggle btn btn-large" href="javascript:void(0)" data-toggle="dropdown">
    With selected
    <b class="caret"></b>
  </a>

  <ul class="dropdown-menu">
    <li>
      <a onclick="return modelActions.execute('delete');" href="javascript:void(0)">Delete</a>
    </li>
    <li>
      <a onclick="return modelActions.execute('edit');" href="javascript:void(0)">Edit</a>
    </li>
  </ul>
</div>

<form id="action_form" action="/admin/fileadmin/action/" method="POST" style="display: none">
  <input type="hidden" id="action" name="action" />
</form>

<script src="/admin/static/admin/js/actions.js"></script>
<script language="javascript">
  var modelActions = new AdminModelActions("Please select at least one file.", {"delete": "Are you sure you want to delete these files?"});
</script>

選擇菜單后的處理方法在actions.js:

var AdminModelActions = function(actionErrorMessage, actionConfirmations) {
  // Actions helpers. TODO: Move to separate file
  this.execute = function(name) {
    var selected = $('input.action-checkbox:checked').size();

    if (selected === 0) {
      alert(actionErrorMessage);
      return false;
    }

    var msg = actionConfirmations[name];

    if (!!msg)
      if (!confirm(msg))
        return false;

    // Update hidden form and submit it
    var form = $('#action_form');
    $('#action', form).val(name);

    $('input.action-checkbox', form).remove();
    $('input.action-checkbox:checked').each(function() {
      form.append($(this).clone());
    });

    form.submit();

    return false;
  };

  $(function() {
    $('.action-rowtoggle').change(function() {
      $('input.action-checkbox').attr('checked', this.checked);
    });
  });
};

對比一下修改前后的表單:

# 初始化
<form id="action_form" style="display: none" method="POST" action="/admin/fileadmin/action/">
  <input id="action" type="hidden" name="action">
</form>

# 'Delete'選中的三個文件
<form id="action_form" style="display: none" method="POST" action="/admin/fileadmin/action/">
  <input id="action" type="hidden" name="action" value="delete">
  <input class="action-checkbox" type="checkbox" value="1.png" name="rowid">
  <input class="action-checkbox" type="checkbox" value="2.png" name="rowid">
  <input class="action-checkbox" type="checkbox" value="3.png" name="rowid">
</form>

# 'Edit'選中的一個文件
<form id="action_form" style="display: none" method="POST" action="/admin/fileadmin/action/">
  <input id="action" type="hidden" name="action" value="edit">
  <input class="action-checkbox" type="checkbox" value="1.png" name="rowid">
</form>

總結(jié)一下,當我們點擊下拉菜單中的菜單項(Delete,Edit),本地JavaScript代碼會彈出確認框(假設有確認信息),然后提交一個表單給/admin/fileadmin/action/,請求處理函數(shù)action_view()根據(jù)表單類型再調(diào)用不同的action處理函數(shù),最后返回一個頁面。

Flask-Admin字段(列)格式化
在某些情況下,我們需要對模型的某個屬性進行格式化。比如,默認情況下,日期時間顯示出來會比較長,這時可能需要只顯示月和日,這時候,列格式化就派上用場了。

比如,如果你要顯示雙倍的價格,你可以這樣做:

class MyModelView(BaseModelView):
  column_formatters = dict(price=lambda v, c, m, p: m.price*2)

或者在Jinja2模板中使用宏:

from flask.ext.admin.model.template import macro

class MyModelView(BaseModelView):
  column_formatters = dict(price=macro('render_price'))

# in template
{% macro render_price(model, column) %}
  {{ model.price * 2 }}
{% endmacro %}

回調(diào)函數(shù)模型:

def formatter(view, context, model, name):
  # `view` is current administrative view
  # `context` is instance of jinja2.runtime.Context
  # `model` is model instance
  # `name` is property name
  pass

正好和上面的v, c, m, p相對應。

相關(guān)文章

  • 解決python讀取幾千萬行的大表內(nèi)存問題

    解決python讀取幾千萬行的大表內(nèi)存問題

    今天小編就為大家分享一篇解決python讀取幾千萬行的大表內(nèi)存問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-06-06
  • Python Web框架之Django框架文件上傳功能詳解

    Python Web框架之Django框架文件上傳功能詳解

    這篇文章主要介紹了Python Web框架之Django框架文件上傳功能,結(jié)合實例形式詳細分析了Django框架form表單基于ajax的文件上傳功能詳細操作技巧,需要的朋友可以參考下
    2019-08-08
  • Python數(shù)據(jù)類型之列表和元組的方法實例詳解

    Python數(shù)據(jù)類型之列表和元組的方法實例詳解

    這篇文章主要介紹了Python數(shù)據(jù)類型之列表和元組的相關(guān)知識,列表是一組有序項目的集合 ,可變的數(shù)據(jù)類型可 進行增刪改查,本文通過實例文字相結(jié)合的形式給大家介紹的非常詳細 ,需要的朋友可以參考下
    2019-07-07
  • Python庫AutoTS一行代碼得到最強時序基線

    Python庫AutoTS一行代碼得到最強時序基線

    AutoTS它是一個用于自動時間序列分析的 Python 庫。AutoTS 允許我們用一行代碼訓練多個時間序列模型,以便我們可以選擇最適合的模型,今天介紹一種非常霸道的工具,融合了自動化機器學習技術(shù)開發(fā)的AutoTS
    2022-03-03
  • python愛心表白 每天都是浪漫七夕!

    python愛心表白 每天都是浪漫七夕!

    每天都是浪漫七夕!這篇文章主要為大家詳細介紹了python愛心表白,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-08-08
  • python保存數(shù)據(jù)到本地文件的方法

    python保存數(shù)據(jù)到本地文件的方法

    今天小編就為大家分享一篇python保存數(shù)據(jù)到本地文件的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-06-06
  • 分享Python?的24個編程超好用技巧

    分享Python?的24個編程超好用技巧

    這篇文章主要給大家分享Python?的24個編程超好用技巧,下面分享一些python技巧和?tips,這些技巧將根據(jù)其首字母按?A-Z?的順序進行展示,具有一定的參考價值,需要的小伙伴可以參考一下
    2022-02-02
  • Python使用apscheduler模塊設置定時任務的實現(xiàn)

    Python使用apscheduler模塊設置定時任務的實現(xiàn)

    本文主要介紹了Python使用apscheduler模塊設置定時任務的實現(xiàn),文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-05-05
  • Python文件簡單操作及openpyxl操作excel文件詳解

    Python文件簡單操作及openpyxl操作excel文件詳解

    這篇文章主要為大家詳細介紹了python對文件的簡單使用及openpyxl操作excel文件的方法,文中的示例代碼講解詳細,感興趣的小伙伴可以了解一下
    2022-10-10
  • pytorch 歸一化與反歸一化實例

    pytorch 歸一化與反歸一化實例

    今天小編就為大家分享一篇pytorch 歸一化與反歸一化實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-12-12

最新評論

桃园县| 香港 | 吉安县| 华阴市| 会宁县| 柘荣县| 榆林市| 固阳县| 贺兰县| 吴江市| 郑州市| 鄂伦春自治旗| 酒泉市| 英吉沙县| 舒兰市| 奎屯市| 潜江市| 凌海市| 敖汉旗| 梁平县| 赤峰市| 丰顺县| 古交市| 岳阳市| 娱乐| 阳原县| 永州市| 永春县| 阜新市| 伊川县| 瓮安县| 滦平县| 保靖县| 疏附县| 辽中县| 麻城市| 合水县| 嘉荫县| 甘谷县| 前郭尔| 芮城县|