1. Flask
1.1. 简介
Flask是使用Python编写的轻量级Web应用框架,其WSGI工具箱采用Werkzeug,模板引擎则使用Jinja2。Flask使用BSD授权,Flask也被称为“microframework”,因为它使用简单的核心,用extension增加其他功能,用户可以随意地进行组合,满足自己的要求。
一段最简单的Flask代码如下:
1 from flask import Flask 2 3 app = Flask(__name__) 4 5 6 7 @app.route(‘/‘) 8 9 def hello_world(): 10 11 return ‘Hello World!‘ 12 13 14 15 if __name__ == ‘__main__‘: 16 17 app.run()
那么,这段代码做了什么?
首先,我们导入了Flask类。这个类的实例将会是我们的WSGI应用程序。
接下来,我们创建一个该类的实例,第一个参数是应用模块或者包的名称。 如果你使用单一的模块(如本例),你应该使用__name__,因为模块的名称将会因其作为单独应用启动还是作为模块导入而有不同( 也即是 ‘__main__‘ 或实际的导入名)。这是必须的,这样 Flask才知道到哪去找模板、静态文件等等。详情见Flask的文档。
然后,我们使用route()装饰器告诉Flask什么样的URL能触发我们的函数。
这个函数的名字也在生成URL时被特定的函数采用,这个函数返回我们想要显示在用户浏览器中的信息。
最后我们用run()函数来让应用运行在本地服务器上。 其中if __name__ == ‘__main__‘: 确保服务器只会在该脚本被Python解释器直接执行的时候才会运行,而不是作为模块导入的时候。
1.2. Flask-SQLAlchemy
Flask-SQLAlchemy是对Flask的一个插件,是Flask对SQLAlchemy的封装,SQLAlchemy提供非常强大ORM(Object Relation Model 对象关系模型)功能。
常见情况下对于只有一个Flask应用,所需要做的事情就是创建Flask应用,选择加载配置,接着创建SQLAlchemy对象时候,把Flask应用传递给它作为参数。
一旦创建,这个对象就包含 sqlalchemy 和 sqlalchemy.orm 中的所有函数和助手。此外它还提供一个名为 Model 的类,用于作为声明模型时的 delarative 基类:
1 class User(db.Model): 2 3 id = db.Column(db.Integer, primary_key=True) 4 5 username = db.Column(db.String(80), unique=True) 6 7 8 9 def __init__(self, username, email): 10 11 self.username = username 12 13 14 15 def __repr__(self): 16 17 return‘<User %r>‘ % self.username
1.2.1. 一对多关系
以资源类型和资源举例说明,它们之间关系是一对多关系,一个资源类型下可以有多个资源,而资源只属于一种类型,如果前面介绍,资源只能是菜单类型或功能类型的一种。
1 class ResourceType(db.Model): 2 3 __tablename__ = ‘SYRESOURCETYPE‘ 4 5 ID = db.Column(db.String(36), primary_key=True) 6 7 CREATEDATETIME = db.Column(db.DateTime, index=True, default=datetime.now) 8 9 UPDATEDATETIME = db.Column(db.DateTime, index=True, default=datetime.now) 10 11 NAME = db.Column(db.String(100)) 12 13 DESCRIPTION = db.Column(db.String(200)) 14 15 resources = db.relationship(‘Resource‘, backref=‘type‘, lazy=‘dynamic‘)
1 class Resource(db.Model): 2 3 __tablename__ = ‘SYRESOURCE‘ 4 5 ID = db.Column(db.String(36), primary_key=True) 6 7 # 省略部分内容 8 9 NAME = db.Column(db.String(100)) 10 11 SYRESOURCETYPE_ID = db.Column(db.String, db.ForeignKey(‘SYRESOURCETYPE.ID‘))
ResourceType类通过relationship关联到Resource类,给自己定义了resources集合属性,代表本类型下的所有资源。db.relationship关联到Resource类,通过backref动态地给Resource类增加了type属性,代表Resource所属的资源类型。
1.2.2. 多对多关系
以用户和角色为例,它们之间的关系是多对多关系,一个用户可以有多个角色,一个角色也可以被多个用户所用。
user_role_table = db.Table(‘SYUSER_SYROLE‘, db.Model.metadata
, db.Column(‘SYUSER_ID‘, db.String, db.ForeignKey(‘SYUSER.ID‘))
, db.Column(‘SYROLE_ID‘, db.String, db.ForeignKey(‘SYROLE.ID‘)))
1 class User(db.Model): 2 3 __tablename__ = ‘SYUSER‘ 4 5 ID = db.Column(db.String(36), primary_key=True) 6 7 LOGINNAME = db.Column(db.String(100), unique=True, index=True) 8 9 PWD = db.Column(db.String(100)) 10 11 NAME = db.Column(db.String(100)) 12 13 14 15 roles = db.relationship(‘Role‘,secondary=user_role_table, 16 17 backref=db.backref(‘users‘, lazy=‘dynamic‘))
1 class Role(db.Model): 2 3 __tablename__ = ‘SYROLE‘ 4 5 ID = db.Column(db.Integer, primary_key=True) 6 7 NAME = db.Column(db.String(100))
User类中通过db.relationship定义了和Role类之间的关系,它们之间连接的桥梁就是user_role_table,user_role_table定义了两张关联表之间主键的对应关系。User对象含有roles集合属性,代表角色的集合,而通过backref动态地给Role对象增加了users属性,代表本角色所从属的用户有哪些。
注:ORM将对象映射到数据表,对象之间的关系映射到表之间的关系,通过操作对象及其之间的关系即可以操作数据库表,非常方便,不需要写SQL语句。
1.3. 集成Python Shell
Flask-Script的shell命令可以导入指定的对象,这样就可以在控制台操作Python对象,譬如用户对象、资源对象等等,即时测试对象的属性和方便,使用起来比较方便。
1 ###########################manager.py#################### 2 3 from app.models import User, Role, Resource, ResourceType, Organization 4 5 from flask_script import Manager, Shell 6 7 from flask_migrate import Migrate, MigrateCommand 8 9 10 11 def make_shell_context(): 12 13 return dict(app=app, db=db, User=User, Role=Role, Resource=Resource, 14 15 ResourceType=ResourceType, Organization=Organization) 16 17 18 19 manager.add_command("shell", Shell(make_context=make_shell_context)) 20 21 manager.add_command(‘db‘, MigrateCommand) 22 23 24 25 @manager.command 26 27 def myprint(): 28 29 print ‘hello world‘
代码使用flask_script下的Manager模块,通过add_command方法将一些对象注册到shell上下文中。注册的方法有两种:
1)add_command。直接调用Manager模块下的add_command方法;
> python manager.py shell
>>> db
<SQLAlchemy engine=‘mysql://root:@127.0.0.1/authbase?charset=utf8‘>
>>> app
<Flask ‘app‘>
>>> User.query.all()
[<User u‘\u8d85\u7ea7\u7ba1\u7406\u5458‘>
, <User u‘\u7ba1\u7406\u5458‘>
]
>>>
2)装饰器方式。使用manager.command装饰器,装饰的方法名称就会成为shell环境中的一条命令。
python manager.py myprint
hello world
1.4. 蓝图
蓝图实现了应用的模块化,使用蓝图使得应用层次更加清楚,开发者可以更容易地开发和维护项目。蓝图通常作用于相同的URL前缀,譬如/admin/user, /admin/resource等等,将一类相同的功能组合到一起,如果URL前缀发生变化,只需要修改蓝图即可。
举例说明,通常网站分为用户网站部分和管理后台部分,如博客类网站,管理后台可以编辑文章等操作,编辑后的文章会在用户网站部分显示,其它用户可以阅读、评论等,同时,管理后台可以评论进行管理等等。按照模块划分:
Blog -------|
| 前台网站
------------|app/blog/:id
------------|app/blog/:id/comment/:id
| 管理后台
|admin/user/:id
|admin/role/:id
|admin/siteinfo
上述Blog将网站分为两个部分:前台网站和管理后台,如果按照蓝图实现,即将应用划分为两个蓝图。
1 from flask import Blueprint 2 3 app = Blueprint(‘app‘, __name__, url_prefix=‘/app‘) 4 5 admin = Blueprint(‘admin‘, __name__, url_prefix=‘/admin‘) 6 7 8 9 @app.route(‘/blog/:id‘, methods=[‘POST‘]) 10 11 defapp_blog(): 12 13 #查询指定编号博客赋值给blog对象 14 15 returnrender_template(blog/index.html‘, blog=blog) 16 17 18 19 @admin.route(‘/role/:id‘, methods=[‘POST‘]) 20 21 defadmin_role(): 22 23 #查询指定编号角色赋值给role对象 24 25 return render_template(role/index.html‘, role=role)