Flask-Admin 是 Flask 的管理插件,可以用于快速创建管理你的 Flask 项目的后台,最常用的功能还是可以很方便得管理
Model,你可以很方便得查看、创建、删除和修改 Model。

这是一个简单的例子,用来创建 Model。

图 1:创建 Model

安装 Flask-Admin

Flask-Admin 的安装很简单,尤其是使用 pip 等管理工具,就更简单了,直接使用以下命令安装:

  1. pip install flask
  2. pip install flask-admin

一个简单的 Flask应用

  1. #!/usr/bin/env python
  2. # encoding: utf-8
  3. from flask import Flask
  4. from flask.ext.admin import Admin
  5. app = Flask(__name__)
  6. admin = Admin(app)
  7. app.run()

直接将这段代码保存为 app.py ,然后直接使用 python 运行

  1. python app.py

接着在浏览器上访问地址:

  1. http://localhost:5000/admin

你将看到一个最简约的 Flask-Admin 的页面。

管理 MongoDB Model 的 Flask-Admin

上面个例子我们可以访问 Flask-Admin 页面了,但是我们并没有管理到任何的数据,所以,这里我们就需要添加管理的数据。

要管理数据,首先需要有数据的结构,然后再指定数据的哪些结构需要管理,可以做哪些操作等等。总结起来,那么我们的操作步骤是:

  1. 创建 Models
  2. 指定可以操作的字段以及可以做的操作
  3. 将 Model 添加到 Flask-Admin 中

下面就先给出一份完整的代码:

  1. #!/usr/bin/env python
  2. # encoding: utf-8
  3. from datetime import datetime
  4. from flask import Flask
  5. from flask_admin import Admin
  6. from flask.ext.mongoengine import MongoEngine
  7. from flask_admin.contrib.mongoengine import ModelView
  8. app = Flask(__name__)
  9. # Create dummy secrey key so we can use sessions
  10. app.config['SECRET_KEY'] = '123456790'
  11. app.config['MONGODB_SETTINGS'] = {'DB': 'testing',
  12. 'host': '192.168.59.103',
  13. 'port': 27017}
  14. # extensions
  15. db = MongoEngine()
  16. admin = Admin(name="Learning", template_mode="bootstrap3")
  17. db.init_app(app)
  18. admin.init_app(app)
  19. # Define mongoengine documents
  20. class User(db.Document):
  21. name = db.StringField(max_length=40)
  22. tags = db.ListField(db.ReferenceField('Tag'))
  23. password = db.StringField(max_length=40)
  24. def __unicode__(self):
  25. return self.name
  26. class Tag(db.Document):
  27. name = db.StringField(max_length=10)
  28. def __unicode__(self):
  29. return self.name
  30. # add model to flask-admin
  31. admin.add_view(ModelView(User))
  32. admin.add_view(ModelView(Tag))
  33. app.run(debug=True)

我们从上到下进行分析,首先 import 的东西就不说了,没太大必要。

然后是前面多出来的初始化语句:

  1. # Create dummy secrey key so we can use sessions
  2. app.config['SECRET_KEY'] = '123456790'
  3. app.config['MONGODB_SETTINGS'] = {'DB': 'testing',
  4. 'host': '192.168.59.103',
  5. 'port': 27017}
  6. # extensions
  7. db = MongoEngine()
  8. db.init_app(app)

这些都是配置 MongoDB 用的,所以和我们的主题没太大关系,可以忽略。因为换成其他数据库(例如 MySQL),那么都会不一样。

接下来的就是:

  1. # Define mongoengine documents
  2. class User(db.Document):
  3. name = db.StringField(max_length=40)
  4. tags = db.ListField(db.ReferenceField('Tag'))
  5. password = db.StringField(max_length=40)
  6. def __unicode__(self):
  7. return self.name
  8. class Tag(db.Document):
  9. name = db.StringField(max_length=10)
  10. def __unicode__(self):
  11. return self.name

这两个是定义 Model 的语句了,也仅仅是定义了 Model 而已,没有其他太大的难点。

唯一需要注意的是 User 引用了 Tag 这个 Model,也就是 User Model 中的这一句:

  1. tags = db.ListField(db.ReferenceField('Tag'))

然后也没其他了。

最后是关键的两句:

  1. # add model to flask-admin
  2. admin.add_view(ModelView(User))
  3. admin.add_view(ModelView(Tag))

这两句才将我们的 Model 添加进 Flask-Admin 的管理之中。

至此,我们一个简单的 Flask-Admin 就算配置完全,可以进行使用了。还是将这个代码保存为 app.py,然后使用 python 运行:

  1. python app.py

依旧访问原来的地址:

  1. http://localhost:5000/admin

你将会看到一个带 User 和 Tag 的 Flask-Admin:

图 2:Flask-Admin

身份验证

因为 Flask-Admin 一般用于后台管理,所以权限相关的内容就非常重要了,所以 Flask-Admin 肯定是带权限管理相关的设置的啦。

在 Flask-Admin 中,控制权限主要依赖于一个方法,这个方法是

  1. is_accessible

同时,还有个相关的方法是:

  1. inaccessible_callback

这个方法用于当没有权限访问时如何处理用户的请求。

下面我就修改一下,修改的目的就是让只有用户名为 “admin” 的用户才能操作我们的 User 和 Tag 的数据。

其实很简单,我们只要继承 ModelView,然后在 is_accessible 方法中做一个限制:

  1. #!/usr/bin/env python
  2. # encoding: utf-8
  3. from datetime import datetime
  4. from flask import Flask
  5. from flask.ext.login import current_user, LoginManager
  6. from flask_admin import Admin
  7. from flask.ext.mongoengine import MongoEngine
  8. from flask_admin.contrib.mongoengine import ModelView
  9. app = Flask(__name__)
  10. # Create dummy secrey key so we can use sessions
  11. app.config['SECRET_KEY'] = '123456790'
  12. app.config['MONGODB_SETTINGS'] = {'DB': 'testing',
  13. 'host': '192.168.59.103',
  14. 'port': 27017}
  15. # extensions
  16. db = MongoEngine()
  17. login_manager = LoginManager()
  18. admin = Admin(name="Learning", template_mode="bootstrap3")
  19. db.init_app(app)
  20. login_manager.init_app(app)
  21. admin.init_app(app)
  22. # Define mongoengine documents
  23. class User(db.Document):
  24. name = db.StringField(max_length=40)
  25. tags = db.ListField(db.ReferenceField('Tag'))
  26. password = db.StringField(max_length=40)
  27. def __unicode__(self):
  28. return self.name
  29. class Tag(db.Document):
  30. name = db.StringField(max_length=10)
  31. def __unicode__(self):
  32. return self.name
  33. class AdminModelView(ModelView):
  34. def is_accessible(self):
  35. if current_user.is_authenticated and current_user.name == "admin":
  36. return True
  37. return False
  38. # add model to flask-admin
  39. admin.add_view(AdminModelView(User))
  40. admin.add_view(AdminModelView(Tag))
  41. @login_manager.user_loader
  42. def load_user(user_id):
  43. return User.objects(id=user_id).first()
  44. app.run(port=5001, debug=True)

相比于第二个版本,我们这里引入了一个新的 Flask-Login 的插件,但是,没关系,这都不是关键,但是,为了防止你走偏,所以我把增加的和 Flask-
Login相关的代码都罗列出来:

  1. from flask.ext.login import current_user, LoginManager
  2. login_manager = LoginManager()
  3. login_manager.init_app(app)
  4. @login_manager.user_loader
  5. def load_user(user_id):
  6. return User.objects(id=user_id).first()

其实和本主题没关系的 Flask-Login 代码就这么多。

然后我们真正为了权限鉴定而修改的代码有:

  1. class AdminModelView(ModelView):
  2. def is_accessible(self):
  3. if current_user.is_authenticated and current_user.name == "admin":
  4. return True
  5. return False
  6. # add model to flask-admin
  7. admin.add_view(AdminModelView(User))
  8. admin.add_view(AdminModelView(Tag))

这里自己集成了 ModelView 类,然后重写了 is_accessible 这个方法,用于判断该用户是否有权限操作我们的
Model,然后,我们用我们自定义的 AdminModelView 来添加 Model,修改完之后,我们再重启一下服务器,

  1. python app

然后再访问一下Flask-Admin 页面,我们看到的将会是这样:

图 3:修改后的 Admin

因为我们没有权限修改 User 和 Tag,所以显示这个很科学。

Q&A

1. BaseView, BaseModelView 与 ModelView 的区别

BaseView: Base administrative view.
AdminIndexView: Default administrative interface index page when visiting the /admin/ URL.
BaseModelView: Base model view.
ModelView: SQLAlchemy model view

2. 修改Admin 首页

  1. 继承 AdminIndexView

    1. class MyHomeView(AdminIndexView):
    2. @expose('/')
    3. def index(self):
    4. arg1 = 'Hello'
    5. return self.render('admin/myhome.html', arg1=arg1)
    6. admin = Admin(index_view=MyHomeView())
  2. 初始化 Admin 添加参数

    1. admin = Admin(
    2. app,
    3. index_view=MyHomeView()
    4. )

    或者,直接在初始化 Admin 时指定

    1. admin = Admin(
    2. app,
    3. index_view=AdminIndexView(
    4. name='Home',
    5. template='admin/myhome.html',
    6. url='/'
    7. )
    8. )

index 页面的默认值分别是:

3. 使用自定义存储

BaseModelView 对 model 如何存储和管理除了以下需求,不做任何其他假定:

  1. 提供的 model 是一个对象
  2. model 包含一系列属性
  3. 每个 model 都包含可以唯一标识自身的属性(例如数据库model的主键)
  4. 可以从数据源中使用分页获取一系列有序的 Model
  5. 可以从数据源中通过标识获取对应的model。

如果想要支持一个新的数据源,你需要做的是以下工作:

  1. 继承 BaseModelView 类。
  2. 实现不同的与数据关联的方法(get_list, get_one, create_model, etc)
  3. 实现自动从 model 表示转换成表单表示的方式

4. 防止 CSRF

To add CSRF protection to the forms that are generated by ModelView instances,
use the SecureForm class in your ModelView subclass by specifying the
form_base_classparameter

  1. from flask_admin.form import SecureForm
  2. from flask_admin.contrib.sqla import ModelView
  3. class CarAdmin(ModelView):
  4. form_base_class = SecureForm

SecureForm requires WTForms 2 or greater. It uses the WTForms SessionCSRF
class to generate and validate the tokens for you when the forms are
submitted.

5. 管理文件和目录

To manage static files instead of database records, Flask-Admin comes with the
FileAdmin plug-in. It gives you the ability to upload, delete, rename, etc.
You can use it by adding a FileAdmin view to your app:

  1. from flask_admin.contrib.fileadmin import FileAdmin
  2. import os.path as op
  3. # Flask setup here
  4. admin = Admin(app, name='microblog', template_mode='bootstrap3')
  5. path = op.join(op.dirname(__file__), 'static')
  6. admin.add_view(FileAdmin(path, '/static/', name='Static Files'))

6. 添加 Redis 控制台

  1. from redis import Redis
  2. from flask_admin.contrib import rediscli
  3. # Flask setup here
  4. admin = Admin(app, name='microblog', template_mode='bootstrap3')
  5. admin.add_view(rediscli.RedisCli(Redis()))