Flask-Admin是一个功能齐全、简单易用的Flask扩展,让你可以为Flask应用程序增加管理界面。它受django-admin包的影响,但用这样一种方式实现,开发者拥有最终应用程序的外观、感觉和功能的全部控制权。
本文是关于Flask-Admin库的快速入门。本文假设读者预先具有一些Flask框架的知识。
- 介绍
- 初始化
- 增加视图
- 身份验证
- 生成URL
- 模型视图
- 文件管理
介绍
这个库打算做到尽可能的灵活。并且开发者不需要任何猴子补丁就可以获得期望的功能。
这个库使用一个简单而强大的概念——管理部件(administrative pieces,不太好翻译),是用视图方法构建的类。
例如,这是一个绝对有效的管理部件:
1
2
3
4
5
6
7
8
|
class MyView(BaseView): @expose ( '/' ) def index( self ): return self .render( 'admin/myindex.html' ) @expose ( '/test/' ) def test( self ): return self .render( 'admin/test.html' ) |
如果用户访问index视图,模板文件admin/myindex.html会被渲染。同样的,访问test视图的结果是admin/test.html被渲染。
那么,这个方法怎样帮助管理界面的结构化?使用这些已建立的部件,你可以实施高度定制化的可重复使用的功能。
例如,Flask-Admin提供一个现成的SQLAlchemy模型接口。它以类执行并接受2个参数:模型类和数据库会话。当它显示一些改变接口的行为的类级变量(有点像django.contrib.admin),没有任何东西阻止你继承它并覆盖表单创建逻辑、数据库存储方法或者通过增加更多的视图扩展现有的功能。
初始化
要开始使用Flask-Admin,你需要创建一个Admin类实例并和Flask应用程序实例关联。
1
2
3
4
5
6
7
8
9
|
from flask import Flask from flask.ext.admin import Admin app = Flask(__name__) admin = Admin(app) # Add administrative views here app.run() |
如果你运行这个程序并访问http://localhost:5000/admin/,你会看到一个顶部有导航栏的空的“Home”页面:
你可以更换应用程序名称通过传值给Admin类构造函数的name参数:
1
|
admin = Admin(app, name = 'My App' ) |
作为一个选择方案,在Admin实例初始化之后,你可以调用init_app()函数把Flask应用程序对象传给Admin构造函数:
1
2
3
|
admin = Admin(name = 'My App' ) # Add views here admin.init_app(app) |
增加视图
现在,让我们增加一个管理视图。下面的例子会致使两个项目出现在导航菜单:Home和Hello。为此,你需要衍生于BaseView类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
from flask import Flask from flask.ext.admin import Admin, BaseView, expose class MyView(BaseView): @expose ( '/' ) def index( self ): return self .render( 'index.html' ) app = Flask(__name__) admin = Admin(app) admin.add_view(MyView(name = 'Hello' )) app.run() |
一个关于管理视图的重要约束是每个视图类应该拥有一个默认的以根URL/开头的页面视图方法。下面的例子是正确的:
1
2
3
4
|
class MyView(BaseView): @expose ( '/' ) def index( self ): return self .render( 'index.html' ) |
可是,这个不工作:
1
2
3
4
|
class MyView(BaseView): @expose ( '/index/' ) def index( self ): return self .render( 'index.html' ) |
现在,创建一个新的index.html文件并写入如下内容:
1
2
3
4
|
{% extends 'admin/master.html' %} {% block body %} Hello World from MyView! {% endblock %} |
然后把它放到templates目录。为维持一致的外观和感觉,所有管理页面应该延伸于admin/master.html模板。
你现在应该看到Hello页面的新的管理页面起作用了。
要增加另一个级别的菜单项目,你可以指定category参数的值当传送管理视图给Admin实例时。category指定顶级菜单项目的名字,并且所有与之关联的视图,都会通过下拉菜单进入。例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
from flask import Flask from flask.ext.admin import Admin, BaseView, expose class MyView(BaseView): @expose ( '/' ) def index( self ): return self .render( 'index.html' ) app = Flask(__name__) admin = Admin(app) admin.add_view(MyView(name = 'Hello 1' , endpoint = 'test1' , category = 'Test' )) admin.add_view(MyView(name = 'Hello 2' , endpoint = 'test2' , category = 'Test' )) admin.add_view(MyView(name = 'Hello 3' , endpoint = 'test3' , category = 'Test' )) app.run() |
看起来是这样的:
要增加另一个级别的菜单项目,你可以指定category参数的值当传送管理视图给Admin实例时。category指定顶级菜单项目的名字,并且所有与之关联的视图,都会通过下拉菜单进入。例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
from flask import Flask from flask.ext.admin import Admin, BaseView, expose class MyView(BaseView): @expose ( '/' ) def index( self ): return self .render( 'index.html' ) app = Flask(__name__) admin = Admin(app) admin.add_view(MyView(name = 'Hello 1' , endpoint = 'test1' , category = 'Test' )) admin.add_view(MyView(name = 'Hello 2' , endpoint = 'test2' , category = 'Test' )) admin.add_view(MyView(name = 'Hello 3' , endpoint = 'test3' , category = 'Test' )) app.run() |
看起来是这样的:
身份验证
Flask-Admin没有设想任何你可以使用的身份验证系统。因此,默认的,管理界面是完全开放的。
要控制使用管理界面,你可以指定is_accessible方法当扩展BaseView类时。那么,举例,如果你使用Flask-Login做身份验证,下面的代码确保只有已登入的用户能访问视图:
1
2
3
|
class MyView(BaseView): def is_accessible( self ): return login.current_user.is_authenticated() |
你也可以实施基于策略的保密,有条件的允许或不允许使用管理界面的某些部分。如果一个用户无权使用某个特定视图,则菜单项目不可见。
生成URL
在内部,视图类工作于Flask蓝图的顶部,因此你可以使用url_for附带一个.前缀来获得局部视图的URL:
1
2
3
4
5
6
7
8
9
10
11
12
|
from flask import url_for class MyView(BaseView): @expose ( '/' ) def index( self ) # Get URL for the test view method url = url_for( '.test' ) return self .render( 'index.html' , url = url) @expose ( '/test/' ) def test( self ): return self .render( 'test.html' ) |
如果你要在外部生成一个特定视图的URL,应用下面的规则:
你可以覆盖endpoint名称通过传送endpoint参数给视图类构造函数:
1
2
3
4
5
6
|
admin = Admin(app) admin.add_view(MyView(endpoint = 'testadmin' )) # In this case, you can generate links by concatenating the view method name with an endpoint: url_for( 'testadmin.index' ) |
如果你不覆盖endpoint名称,类名的小写形式会用于生成URL,像这样:
url_for('myview.index')
对基于模型的视图规则不一样——模型类名称会被使用如果没有提供endpoint名称。基于模型的视图下一节解释。
模型视图
模型视图允许你为数据库中的每个模型增加专用的管理页面。通过创建ModelView类实例做这个,ModelView类可从Flask-Admin内置的ORM后端引入。一个SQLAlchemy后端的例子,你可以这样使用:
1
2
3
4
5
6
|
from flask.ext.admin.contrib.sqla import ModelView # Flask and Flask-SQLAlchemy initialization here admin = Admin(app) admin.add_view(ModelView(User, db.session)) |
这创建一个User模型的管理界面。默认的,列表视图看起来是这样的:
要定制这些模型视图,你有两个选择:一是覆盖ModelView类的公有属性,二是覆盖它的方法。
例如,假如你要禁用模型创建功能并且只在列表视力显示某些列,你可以这样做:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
from flask.ext.admin.contrib.sqla import ModelView # Flask and Flask-SQLAlchemy initialization here class MyView(ModelView): # Disable model creation can_create = False # Override displayed fields column_list = ( 'login' , 'email' ) def __init__( self , session, * * kwargs): # You can pass name and other parameters if you want to super (MyView, self ).__init__(User, session, * * kwargs) admin = Admin(app) admin.add_view(MyView(db.session)) |
覆盖表单元素有些棘手,但还是可能的。这个例子是关于如何建立一个包含有只允许使用预定义值的名为status的列的表单,并使用SelectField:
1
2
3
4
5
6
7
8
9
|
from wtforms.fields import SelectField class MyView(ModelView): form_overrides = dict (status = SelectField) form_args = dict ( # Pass the choices to the `SelectField` status = dict ( choices = [( 0 , 'waiting' ), ( 1 , 'in_progress' ), ( 2 , 'finished' )] )) |
通过继承BaseModelView类和实现数据库相关的方法为不同的数据库后端(比如Mongo等)增加支持是相对容易的。
关于如何定制基于模型的管理视图的行为请参考flask.ext.admin.contrib.sqla文档。
文件管理
Flask-Admin拥有另一个便利的特性——文件管理。它给予你管理服务器文件的能力(上传、删除、重命名等)。
这是一个简单的例子:
1
2
3
4
5
6
7
8
9
10
|
from flask.ext.admin.contrib.fileadmin import FileAdmin import os.path as op # Flask setup here admin = Admin(app) path = op.join(op.dirname(__file__), 'static' ) admin.add_view(FileAdmin(path, '/static/' , name = 'Static Files' )) |
例子截图:
你可以禁用上传、禁用文件或目录删除、限制文件上传类型等等。关于怎么做这些请查看flask.ext.admin.contrib.fileadmin文档。