通过上面的内容,我们已经构建出了一个 CMS 的雏形,但是还缺了最重要的一层——展示层。 这里我们使用当下流行的前后端分离的方式去做,输出 Restful 风格的 API 供前端使用。
Restful 风格的 API 说白了其实就是针对一个 Resource 用 HTTP 协议里已经有了定义的 Method 来进行增删改查。比如我们的 Post 接口,我们需要能够看到 Post 列表,能够看到 Category 列表,能够看到 Post 的详细内容。但是我们不能删除这些内容,因为我们的 API 是给客户端用的,而我们的观众是没有这个权限干这个事情的。
所以,到这里明确了我们要做的事情,我们需要以下几个 API:
Post Listing (/post)
Post Detail (/post/postID)
Category Listing (/category)
Post Listing by category (/category/categoryID/post)
一、前言
Flask 是基于 Werkzeug 的一个小型框架,小到都不应该把 Flask 叫框架(Framework),因为它没有像常见框架那样集成 ORM,只有薄薄的请求处理和模板渲染功能。
但我们不能因为 Flask 小,就轻视它,认为它是一个玩具框架,不能用于生产实践。相反,正是因为它结构松散,没有绑定多余的东西,因此它足够灵活,你可以根据自己的需要,去集成任何你想要的东西。
Python 庞大的生态,加上Flask丰富的扩展,在用 Flask 做 Web 开发的过过程中,几乎可以轻易地找到大部分想要的“轮子”,从而可以让开发人员专注于业务功能,实现产品的快速开发。
本文会用一个简单的 CMS 系统为例,为大家分享如何在一个小时之内,利用 Flask 框架快速地搭建起一个带管理后台和 API 的简单 CMS 系统。
另外,本文是入门教程,主旨是分享 Flask 框架的使用以及其丰富的周边生态,让大家对 Flask 开发 Web 的方便和快捷有个基本的了解,因此会更注重于看得见摸得着的开发实践,而不会对框架的源码做详细深入的介绍。所以,暂时略去了关于 WSGI 和 Werkzeug 的介绍,有兴趣的可以看下官方文档。
二、快速开始
要想构建一个CMS(内容管理系统),最重要的东西是什么呢?毫无疑问,就是文章和文章分类,那我们就以这两点为基础,进行我们的开发构建。
2.1 基础环境搭建
因为是 CMS 系统,所以数据库是免不了的,这里使用 SQLAlchemy 作为数据库的 ORM 层。具体安装非常简单,一句话就可以搞定:
pip install Flask Flask-SQLAlchemy
2.2 Quick Demo
以上是每一个框架所必备的 Hello World 示例。可以看到,使用 Flask 的入门成本是极低的,没有冗余繁琐的配置和需要事先了解的一大堆的范式,只有简单的路由定义和一目了然的响应方式,让我们可以用极快的速度上手开发。
2.3 表模型创建
下面我们直接进入正题,开始真正的 CMS 的开发。
使用 SQLAlchemy 来定义表模型是十分简单的,SQLAlchemy 抽象除了各种数据结构映射到对应的数据库的各种类型的字段。为了演示方便,这里是用 SQLite 数据库,声明数据库的地址直接在 Flask 实例化的 app 里进行配置即可:
下面是我们要定义的表模型:
然后是利用 Flask 自带的 hook,在首次响应的时候,自动创建数据库表模型:
在我们访问了一次
http://127.0.0.1:5012
以后,会自动在当下目录生成 test.db 文件,这个就是我们的数据库了。三、 快速构建管理控制后台
有了数据库表模型以后,我们想的第一件事情应该就是往数据库里塞一点测试数据,然后供我们继续的开发测试用了。这个时候,有的同学就会找一个数据库客户端,然后打开我们刚才生成的 test.db 文件,然后找到我们刚刚创建的 category 和 post 这两张表,往里面插入数据了。
接下来我是不是要说这个呢?
看我们的小标题就知道了,肯定不是!
我们要做的是在创建完表模型以后,快速得构建出一个管理后台,让我们能够通过这个管理后台去做生成和修改测试数据的事情。但是,天天写增删改查这种逻辑任谁也受不了,所以我们需要一个比较无痛的方法省掉我们那些增删改查的代码,把我们从重复中解放出来。
OK,下面祭出我们的 Flask 界的后台生成神器:Flask-Admin
这是一款前端基于 bootstrap 的后台管理工具,安装同上,依然是很简单的
pip install Flask-Admin
就可以搞定。在代码中的引入和实例化也和 SQLAlchemy 类似
细心的同学看到这个地方就会发现,Flask的扩展的命名和引入方式大多都是一样的,先引入扩展,然后实例化的时候传入 app 实例,使之与Flask 绑定。这里有个注意点,就是 Flask-Admin 使用的时候需要我们去配置 SECRET_KEY 这个参数,如果不定义的话,是没办法在后台进行表单提交的。配置方法很简单,在上面配置 SQLALchemy 的地方,再补充一句配置就可以了,示例如下:
app.config['SECRET_KEY'] = '!!!-----www.baixing.com-----!!!!'
既然是自动生成后台的工具了,那么必然有一个生成的依据,这个依据就是我们刚刚定义的 SQLAlchemy 模型,在 Flask-Admin 中,我们可以直接使用由 SQLAlchemy 定义的模型来生成增删改查的页面。
先引入适配 SQLAlchemy 的 ModelView
from flask_admin.contrib.sqla import ModelView
最后一步,创建我们需要的管理页面:
接下来访问
http://127.0.0.1:5012/admin/
看下效果后台列表页截图
后台编辑页截图
Awesome!!
一个带着增删改查全功能的后台就这么出来了,而且从上图可以看到,在创建 Post 的时候,Flask-Admin 可以自动识别外键,直接将 category 和 我们的 category 模型进行关联,而显示的内容,正是我们定义 Category 这个模型的时候,定义在
__repr__
这个魔术方法中的返回值。上面的例子里,我们代码基本都是通过配置的方式生成的,虽然很快速,但有的同学一定会有这样的疑惑,就是我们用的
Flask-Admin
,会不会因为自动生成的缘故,导致了难以实现定制,从而降低了应用的可扩展性。可以肯定的说,自动生成必然会带来可扩展性的降低,但是 Flask-Admin 给我们预留的扩展的口子还是很多的。从模板层面来讲,因为是基于 Bootstrap 的,所以你几乎可以无痛地将模板更换层任何你想用的 Bootstrap 模板,笔者就曾经换了一套 AdminLTE2 模板 (https://github.com/couldtt/flask-admin) 用在公司一个项目的开发上。
另外,在使用 Flask-Admin 的时候,我们可以不局限于 ModelView 这种方式,把 Flask-Admin 当做一种页面布局的方式。通过 ModelView 和 CustomView 混合布局的方式,适合直接用模型生成的就直接生成,不合适的就略做调整,通过重定义各类 form 操作的方法来实现部分的自定义,如果还是不合适,那就直接重写界面,Flask-Admin 提供了一个 expose 方法,用途和 Flask 中的 route 方法类似,可以用来定义 Admin 的路由。
最后,顺便提一句,Airbnb 开源的数据流管理工具 airflow (https://github.com/apache/incubator-airflow) 也是基于Flask-Admin 开发的喔~
四、 快速构建 Restful 风格的 API
通过上面的内容,我们已经构建出了一个 CMS 的雏形,但是还缺了最重要的一层——展示层。 这里我们使用当下流行的前后端分离的方式去做,输出 Restful 风格的 API 供前端使用。
Restful 风格的 API 说白了其实就是针对一个 Resource 用 HTTP 协议里已经有了定义的 Method 来进行增删改查。比如我们的 Post 接口,我们需要能够看到 Post 列表,能够看到 Category 列表,能够看到 Post 的详细内容。但是我们不能删除这些内容,因为我们的 API 是给客户端用的,而我们的观众是没有这个权限干这个事情的。
所以,到这里明确了我们要做的事情,我们需要以下几个 API:
对于 Listing 类型的接口,我们通常需要额外增加一个翻页的功能,以供内容足够多的时候进行分块的显示。
既然说了是快速构建,那这里肯定也不是手写一堆查询的逻辑和分页的逻辑了,依然是用配置为主少量自定义为辅的方式进行。
这里再引入一个 Flask 里很好用的扩展:Flask-Restless
安装同上
pip install Flask-Restless
使用上也和 Flask-Admin 雷同
至此,我们需要的四个查询接口,都已经被生成了。访问方式是 URL 后面加个 api ,然后接上我们的 post 或者 category 即可。
关于 Post Listing, Post Detail, Category Listing 这三个接口的返回,因为很简单,这里不再赘述,着重说一下 Post Listing by category 这个接口。
看 URL 就可以猜出,这个接口的目的在于返回某个分类下的文章,而我们什么都没做,只是定义了 Post 和 Category 这两个 Resource 而已,直接访问
/api/category/categoryId/post
就可以获取下面的结果,而这其中的奥秘就在于我们上面定义 Post 这个模型的时候,定义的 Relationshipcategory = db.relationship('Category', backref='post')
, backref 的全称是 “back reference”,反向引用的意思。Flask-Restless 可以直接读取这个引用关系,直接应用到我们创建的 API 上面去,可谓十分方便。Post Listing by category 的返回结果:
从上面的返回结果中看,分页已经被处理了,我们在使用 GET 语义的接口获取 Listing 相关的内容时,是不需要去关心 total 以及 offset 等分页的逻辑的,拿来就用即可。
另外,Flask-Restless 的功能远远不止这些, Flask-Restless 在每一个 HTTP Verb 上,都定义了对应的 preprocessor 和 postprocessor,你可以很轻松得使用面向切面编程的方式去插入各种中间件,针对特定的业务需求引入自己的业务逻辑。
五、后记
作为一个 CMS , 上面的例子还很原始,它还缺少很多基本的功能,比如作者管理,授权等等。但是从这个例子出发,稍微扩展一下,加一加模型,在熟悉 Flask 开发的套路以后,将其补充成一个功能完备的 CMS 系统并不困难。
Flask、Flask-SQLAlchemy、Flask-Admin 和 Flask-Restless 进行配合,可以很轻松的实现我们日常开发中一些常见的 xxx 系统,尤其是对于性能等要求不高的内部系统,尤为合适。对于初创产品的构建,使用这个组合也可以很好地满足需求,既能实现快速开发的目标,同时又保留了扩展的空间,可以方便我们后期对项目进行重构和迁移。
此外,本文使用的 Python 环境为 Python3.4,文中代码可以访问如下地址获取:
https://gist.github.com/couldtt/22583f6f4cb303a29c2b004484afe9ab
六、参考资料