Flask常用扩展用法(二)

Author Avatar
patrickcty 9月 23, 2017

Flask-Restful

具体内容参考之前的文章

Flask-Celery

这个用法比较多,之前的两篇文章也有总结过,可以参考之前的内容:

Flask-Debugtool

如同这个扩展的名字一样,这个扩展的作用就是为了使调试 Flask 应用变得更加的轻松愉快。

注意:这个扩展只有在配置文件的 Debug = True 的时候才会生效。

初始化

from flask_debugtoolbar import DebugToolbarExtension

debug_toolbar = DebugToolbarExtension()
debug_toolbar.init_app(app)

重定向

默认情况下,Flask Debug Tool 会拦截 HTTP 302 重定向请求,修改配置文件来禁止这个行为:

DEBUG_TB_INTERCEPT_REDIRECTS = False

Flask-Cache

一个强大的缓存工具,可以有效减少数据库等的查询次数,从而极大程度加快页面的访问速度。

初始化

from flask_cache import Cache

cache = Cache()
cache.init_app(app)

配置缓存输出结果

这里是把结果保存在内存的一个 Python 字典里面

CACHE_TYPE = 'simple'

缓存函数和视图

缓存视图函数

只用在函数上添加一个装饰器就可以缓存这个视图函数的输出结果了:

@blog_print.route('/')
@blog_print.route('/<int:page>')
@cache.cached(timeout=60)
def home(page=1):
    posts = Post.query.order_by(
        Post.publish_time.desc()
    ).paginate(page, 10)
    recent, top_tags = sidebar_data()

    return render_template(
        'home.html',
        posts=posts,
        recent=recent,
        top_tags=top_tags
    )

timeout 表示结果缓存的时长,超过这个时长就会再次执行函数取得新的缓存结果。

缓存不带参数的函数

非视图函数也可以缓存,不过要加入 key_prefix 参数,这个参数用来表示函数的运行结果。

@cache.cached(timeout=7200, key_prefix='sidebar_data')
def sidebar_data():
    recent = Post.query.order_by(
        Post.publish_time.desc()
    ).limit(5).all()
    # 下面这个查询不是很理解,join 和 group_by 这两个的用法没怎么接触过
    top_tags = db.session.query(
        Tag, func.count(tags.c.post_id).label('total')
    ).join(
        tags
    ).group_by(Tag).limit(5).all()

    return recent, top_tags

缓存带参数的函数

对于需要接受参数的函数,就不能直接加上装饰器了,不然每次运行的结果都是相同,这时候我们可以使用 memorize 函数来解决这个问题。

memorize 函数不但会储存函数的运行结果,还会储存调用时的参数,括号里面表示的也是缓存的秒数。

@staticmethod
@cache.memoize(60)
def verify_auth_token(token):
    s = Serializer(current_app.config['SECRET_KEY'])

    try:
        data = s.loads(token)
    except SignatureExpired:
        return None
    except BadSignature:
        return None
    user = User.query.get(data['id'])
    return user

使用 redis 作为缓存后端

redis 我们在 Flask-Celery 里面用到过,用来储存消息队列相关内容,在这里我们也可以利用 redis 来缓存更多的结果。

只用修改配置文件:

class ProdConfig(Config):
    CACHE_TYPE = 'redis'
    CACHE_REDIS_HOST = 'localhost'
    CACHE_REDIS_PORT = '6379'
    CACHE_REDIS_PASSWORD = ''
    CACHE_REDIS_DB = '0'

Flask-Admin

强大的管理员管理界面,省去了不少造轮子的功夫。

初始化

from flask_admin import Admin

admin = Admin()
admin.init_app(app)

初始化之后这个扩展就自动在 localhost:5000/admin 处显示出了相应的管理员页面。

编写基础管理页面

BaseView 是用来在 admin 的主界面添加相应的视图选项。

现在我们在 localhost:5000/admin 之后添加一个 customview 的选项。例如生成 localhost:5000/admin/customview/ 的路径

由于也要创建视图,所以在视图函数所在的文件/文件夹 里面加入以下的代码:

from flask_admin import BaseView, expose

class CustomView(BaseView):
    @expose('/')
    @login_required
    @admin_permission.require(http_exception=403)
    def index(self):
        return self.render('admin/custom.html')

    @expose('/second_page')
    @login_required
    @admin_permission.require(http_exception=403)
    def second_page(self):
        return self.render('admin/second_page.html')

这个类名就是默认的路由的名字(localhost:5000/admin/customview/)了,还可以定义更多的子路由,定义的方法就是使用 expose 装饰器。

注意这里必须要有一个暴露‘/’路由的方法来作为选项的根路径。

为了保持风格的统一性,我们的模板也应该继承 Flask Admin 的默认样式:

{% extends 'admin/master.html' %}
{% block body %}
    Test custom view.
    <a href="{{ url_for('.second_page') }}">Link</a>
{% endblock %}

基本工作已经做完了,但是要让这些生效,必须在 admin 对象上注册一个 CustomView 的实例,这个是在 create_app 函数中完成的,而不是在 extensions 中。因为渲染管理页面需要用到数据库对象,如果放在 extensions 则可能会发生循环导入。

# name 参数用来表示导航条中的文本
admin.add_view(CustomView(name='Custom'))

编写数据库管理页面

Flask Admin 最强大的地方就是它可以直接管理 SQLAlchemy 模型,让对数据库的操作直接在 GUI 中完成。

完成这样的功能同样也需要在视图函数文件处进行初始化:

from flask_admin.contrib.sqla import ModelView
from flask_admin.contrib.fileadmin import FileAdmin

class CustomModelView(ModelView):
    pass

也要在 create_app 的地方注册,这里要把所需要的表注册进去:

models = [User, Reminder, Post, Role, Comment, Contact, Tag]

for model in models:
    # category 参数相同的部分就会放在导航栏的同一个下拉菜单中
    admin.add_view(CustomModelView(model, db.session, category='Models'))

编写文件系统管理页面

除了直接访问数据库内容,Flask Admin 还可以从网页访问服务器的文件系统。

首先也需要在视图函数文件处进行初始化

from flask_admin.contrib.fileadmin import FileAdmin

class CustomFileAdmin(FileAdmin):
    pass

然后只需要把 CustomFileAdmin 类注册到 admin 中就可以访问了:

admin.add_view(CustomFileAdmin(
    os.path.join(os.path.dirname(__file__), 'static'),
    '/static/',
    name='Static Files'
))

保护 Flask Admin 的安全

既然是 Admin,那自然只有管理员才能访问了,所以我们要加入相应的权限。

首先也应该在自定义路由加入 login_require, admin_permission.require() 装饰器来声明权限:

class CustomView(BaseView):
    @expose('/')
    @login_required
    @admin_permission.require(http_exception=403)
    def index(self):
        return self.render('admin/custom.html')

    @expose('/second_page')
    @login_required
    @admin_permission.require(http_exception=403)
    def second_page(self):
        return self.render('admin/second_page.html')

再就是在 MobileView 和 FileAdmin 中定义 is_accessible 方法来控制权限:

class CustomModelView(ModelView):
    def is_accessible(self):
        return current_user.is_authenticated and admin_permission.can()
        
        
class CustomFileAdmin(FileAdmin):
    def is_accessible(self):
        return current_user.is_authenticated and admin_permission.can()

最后

常用的扩展差不多就是这么多了,Flask 的一个强大的地方就是它自定义扩展很容易,所以市面上 Flask 扩展有很多,实现的功能也很全面,就进一步减轻了完成一个 Flask App 的工作量。