Flask【数据库总结】
数据库
在Flask中使用mysql数据库,需要安装一个flask-sqlalchemy的扩展。
pip install flask-sqlalchemy
要连接mysql数据库,仍需要安装flask-mysqldb
pip install flask-mysqldb
Flask的数据库设置:
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:mysql@127.0.0.1:3306/test3'
常用的SQLAlchemy字段类型
| 类型名 | python中类型 | 说明 |
|---|---|---|
| Integer | int | 普通整数,一般是32位 |
| SmallInteger | int | 取值范围小的整数,一般是16位 |
| BigInteger | int或long | 不限制精度的整数 |
| Float | float | 浮点数 |
| Numeric | decimal.Decimal | 普通整数,一般是32位 |
| String | str | 变长字符串 |
| Text | str | 变长字符串,对较长或不限长度的字符串做了优化 |
| Unicode | unicode | 变长Unicode字符串 |
| UnicodeText | unicode | 变长Unicode字符串,对较长或不限长度的字符串做了优化 |
| Boolean | bool | 布尔值 |
| Date | datetime.date | 时间 |
| Time | datetime.datetime | 日期和时间 |
| LargeBinary | str | 二进制文件 |
常用的SQLAlchemy列选项
| 选项名 | 说明 |
|---|---|
| primary_key | 如果为True,代表表的主键 |
| unique | 如果为True,代表这列不允许出现重复的值 |
| index | 如果为True,为这列创建索引,提高查询效率 |
| nullable | 如果为True,允许有空值,如果为False,不允许有空值 |
| default | 为这列定义默认值 |
常用的SQLAlchemy关系选项
| 选项名 | 说明 |
|---|---|
| backref | 在关系的另一模型中添加反向引用 |
| primary join | 明确指定两个模型之间使用的联结条件 |
| uselist | 如果为False,不使用列表,而使用标量值 |
| order_by | 指定关系中记录的排序方式 |
| secondary | 指定多对多中记录的排序方式 |
| secondary join | 在SQLAlchemy中无法自行决定时,指定多对多关系中的二级联结条件 |
数据库基本操作
在Flask-SQLAlchemy中,插入、修改、删除操作,均由数据库会话管理。会话用db.session表示。在准备把数据写入数据库前,要先将数据添加到会话中然后调用commit()方法提交会话。
数据库会话是为了保证数据的一致性,避免因部分更新导致数据不一致。提交操作把会话对象全部写入数据库,如果写入过程发生错误,整个会话都会失效。
数据库会话也可以回滚,通过db.session.rollback()方法,实现会话提交数据前的状态。
在Flask-SQLAlchemy中,查询操作是通过query对象操作数据。最基本的查询是返回表中所有数据,可以通过过滤器进行更精确的数据库查询。
常用的SQLAlchemy查询过滤器
| 过滤器 | 说明 |
|---|---|
| filter() | 把过滤器添加到原查询上,返回一个新查询 |
| filter_by() | 把等值过滤器添加到原查询上,返回一个新查询 |
| limit | 使用指定的值限定原查询返回的结果 |
| offset() | 偏移原查询返回的结果,返回一个新查询 |
| order_by() | 根据指定条件对原查询结果进行排序,返回一个新查询 |
| group_by() | 根据指定条件对原查询结果进行分组,返回一个新查询 |
常用的SQLAlchemy查询执行器
| 方法 | 说明 |
|---|---|
| all() | 以列表形式返回查询的所有结果 |
| first() | 返回查询的第一个结果,如果未查到,返回None |
| first_or_404() | 返回查询的第一个结果,如果未查到,返回404 |
| get() | 返回指定主键对应的行,如不存在,返回None |
| get_or_404() | 返回指定主键对应的行,如不存在,返回404 |
| count() | 返回查询结果的数量 |
| paginate() | 返回一个Paginate对象,它包含指定范围内的结果 |
示例代码:
1 # coding:utf-8 2 from flask import Flask 3 4 from flask_sqlalchemy import SQLAlchemy 5 6 # 创建应用实例 7 app = Flask(__name__) 8 9 # 创建连接数据库的uri 10 app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:mysql@127.0.0.1:3306/flask_demo' 11 12 13 # 设置每次请求后会自动提交到数据库的改动 14 app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True 15 16 # 将启动应用时的提示信息关闭 17 app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True 18 19 # csrf 20 21 # 创建数据库实例 22 db = SQLAlchemy(app) 23 24 class Role(db.Model): 25 __tablename__ = 'roles' 26 # 主键 27 id = db.Column(db.Integer, primary_key=True) 28 # 姓名,字符串, 此字段不能有重复 29 name = db.Column(db.String(64), unique=True) 30 us = db.relationship('User', backref='role') 31 32 33 class User(db.Model): 34 __tablename__ = 'users' 35 # 主键 36 id = db.Column(db.Integer, primary_key=True) 37 # 姓名 38 name = db.Column(db.String(64), unique=True, index=True) 39 # 邮箱 40 email = db.Column(db.String(64), unique=True) 41 # 密码 42 pwd = db.Column(db.String(64)) 43 44 role_id = db.Column(db.Integer, db.ForeignKey('roles.id')) 45 46 47 def __repr__(self): 48 return self.name 49 50 51 52 if __name__ == '__main__': 53 db.drop_all() 54 db.create_all() 55 ro1 = Role(name='admin') 56 ro2 = Role(name='user') 57 db.session.add_all([ro1, ro2]) 58 db.session.commit() 59 60 # 创建模型的实例 61 use1 = User(name='wang', email='wang163@.com', pwd='123456', role_id = ro1.id) 62 use2 = User(name='li', email='li126@.com', pwd='xiaolizi', role_id=ro2.id) 63 use3 = User(name='zhang', email='zhangqq@.com', pwd='zhangsan', role_id=ro1.id) 64 use4 = User(name='xiaosong', email='mourong@.com', pwd='zherrrr', role_id=ro2.id) 65 # 添加到会话当中 66 db.session.add_all([use1, use2, use3, use4]) 67 # 提交操作 68 db.session.commit() 69 app.run(debug=True)
将数据添加到会话中示例:
user = User(name='python') db.session.add(user) db.session.commit()
创建表:
db.create_all()
删除表
db.drop_all()
插入一条数据
ro1 = Role(name='admin') db.session.add(ro1) db.session.commit() #再次插入一条数据 ro2 = Role(name='user') db.session.add(ro2) db.session.commit()
一次插入多条数据
us1 = User(name='wang',email='wang@163.com',pswd='123456',role_id=ro1.id) us2 = User(name='zhang',email='zhang@189.com',pswd='201512',role_id=ro2.id) us3 = User(name='chen',email='chen@126.com',pswd='987654',role_id=ro2.id) us4 = User(name='zhou',email='zhou@163.com',pswd='456789',role_id=ro1.id) db.session.add_all([us1,us2,us3,us4]) db.session.commit()
查询
filter_by精确查询
返回名字等于wang的所有人
User.query.filter_by(name='wang').all()
first()返回查询到的第一个对象
User.query.first()
all()返回查询到的所有对象
User.query.all()
filter模糊查询,返回名字结尾字符为g的所有数据。
User.query.filter(User.name.endswith('g')).all()
get(),参数为主键,如果主键不存在没有返回内容
User.query.get()
逻辑非,返回名字不等于wang的所有数据。
User.query.filter(User.name!='wang').all()
逻辑与,需要导入and,返回and()条件满足的所有数据。
from sqlalchemy import and_
User.query.filter(and_(User.name!='wang',User.email.endswith('163.com'))).all()
逻辑或,需要导入or_
from sqlalchemy import or_
User.query.filter(or_(User.name!='wang',User.email.endswith('163.com'))).all()
not_ 相当于取反
from sqlalchemy import not_ User.query.filter(not_(User.name=='chen')).all()
查询数据后删除
user = User.query.first() db.session.delete(user) db.session.commit() User.query.all()
更新数据
user = User.query.first() user.name = 'dong' db.session.commit() User.query.first()
关联查询示例:角色和用户的关系是一对多的关系,一个角色可以有多个用户,一个用户只能属于一个角色。
查询角色的所有用户:
#查询roles表id为1的角色 ro1 = Role.query.get(1) #查询该角色的所有用户 ro1.us
查询用户所属角色:
#查询users表id为3的用户 us1 = User.query.get(3) #查询用户属于什么角色 us1.role
数据库迁移
数据库迁移配置的步骤
# coding=utf-8 from flask import Flask from flask_sqlalchemy import SQLAlchemy #1.导入migrate扩展,主要用来实现数据库的迁移 from flask_migrate import Migrate,MigrateCommand #2.命令行扩展,通过和migrate配合使用,可以实现数据库表的创建,以及数据库的迁移备份 from flask_script import Manager app = Flask(__name__) #3.创建命令行的对象,替代app的启动服务器的作用,并不能够替代装饰器/配置文件等 manager = Manager(app) app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:mysql@localhost/dbs' app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True db = SQLAlchemy(app) #4.可以Migrate(app,db)这样使用,把应用程序实例和数据库实例添加进来 migrate = Migrate(app,db) #5.以命令的形式实现数据库迁移备份,第一个参数是数据库实例 manager.add_command('db',MigrateCommand) class Role(db.Model): __tablename__ = 'roles' id = db.Column(db.Integer,primary_key=True) name = db.Column(db.String(32),nullable=True) # user = db.relationship('User',backref='role') def __repr__(self): return 'role:%s' % self.name class User(db.Model): __tablename__ = 'users' id = db.Column(db.Integer,primary_key=True) name = db.Column(db.String(32)) # email = db.Column(db.String(32),unique=True) # pswd = db.Column(db.String(32)) # role_id = db.Column(db.Integer,db.ForeignKey('roles.id')) def __repr__(self): return 'user:%s' % self.name @app.route('/') def index(): db.drop_all() db.create_all() ro1 = Role(name='admin') ro2 = Role(name='user') db.session.add_all([ro1, ro2]) db.session.commit() us1 = User(name='WangWu', email='wang@163.com', pswd='123456', role_id=ro1.id) us2 = User(name='ZhangSan', email='zhang@189.com', pswd='201512', role_id=ro2.id) us3 = User(name='ChenQi', email='chen@126.com', pswd='987654', role_id=ro2.id) us4 = User(name='ZhouLiu', email='zhou@163.com', pswd='456789', role_id=ro1.id) db.session.add_all([us1, us2, us3, us4]) db.session.commit() return 'database create success' if __name__ == '__main__': manager.run()
相关命令
创建migrations文件夹,相关 迁移文件存在该文件夹下的versions文件夹下面,此时迁移文件还没有产生
python database.py db init

生成迁移脚本
python database.py db migrate -m 'initial migration'
更新数据库
python database.py db upgrade
查看历史版本
python database.py db history
回退数据库
python database.py db downgrade 版本号
蓝图
随着业务代码的增加,将所有代码都放在单个程序文件中,是非常不合适的。这不仅会让代码阅读变得困难,而且会给后期维护带来麻烦。
解决问题:
将一个.py文件中的代码模块化,降低各功能之间的耦合度,方便维护
解决工具:
蓝图类 Blueprint()
解决步骤:
1 # coding:utf-8 2 from flask import Flask 3 4 app = Flask(__name__) 5 6 @app.route('/') 7 def index(): 8 return 'index' 9 10 @app.route('/user/') 11 def user(): 12 return 'user' 13 14 @app.route('/register/') 15 def register(): 16 return 'register' 17 18 # 3.导入蓝图名字 19 from k_blue_print_du import du 20 # 4.注册蓝图实例 参数一为蓝图实例的名字, 参数二一般为空 21 app.register_blueprint(du, url_prefix='') 22 23 24 if __name__ == '__main__': 25 print app.url_map 26 app.run(debug=True)
1 # coding:utf-8 2 from flask import Blueprint 3 4 # 1.创建蓝图, 参数一为制定蓝图的名字 5 du = Blueprint('du', __name__) 6 7 # 2.修改装饰器的名字 将app的名字该为蓝图的名字 8 @du.route('/del/') 9 def dell(): 10 return 'del success' 11 12 @du.route('/up/') 13 def up(): 14 return 'up success'
在操作数据库文件当中导入启动程序文件当中的属性时尽量写在创建蓝图对象的下面,否则会报错
单元测试:
# coding:utf-8 import unittest from flask import current_app #自定义测试类,setUp方法和tearDown方法会分别在测试前后执行。以test_开头的函数就是具体的测试代码。 from author import * class db_test(unittest.TestCase): def setUp(self): app.config['TESTING'] = True app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:mysql@localhost/flask_demo' self.app = app db.create_all() def tearDown(self): db.session.remove() db.drop_all() def test_append_data(self): au_xi = Author(name='我吃西红柿', email='wang@126.com') # 向数据库当中插入数据 bk_xi = Book(info='吞噬星空', leader='xxx', au_book=au_xi.id) # 向数据库当中插入数据 db.session.add_all([au_xi, bk_xi]) db.session.commit() aut = Author.query.filter_by(name='我吃西红柿').first() bk = Book.query.filter_by(info='吞噬星空') self.assertIsNotNone(aut) self.assertIsNotNone(bk)
Restful
2000年,Roy Thomas Fielding博士在他的博士论文《Architectural Styles and the Design of Network-based Software Architectures》中提出了几种软件应用的架构风格,REST作为其中的一种架构风格在这篇论文中进行了概括性的介绍。
REST:Representational State Transfer的缩写,翻译:“具象状态传输”。一般解释为“表现层状态转换”。
REST是设计风格而不是标准。是指客户端和服务器的交互形式。我们需要关注的重点是如何设计REST风格的网络接口。
- REST的特点:
-
具象的。一般指表现层,要表现的对象就是资源。比如,客户端访问服务器,获取的数据就是资源。比如文字、图片、音视频等。
-
表现:资源的表现形式。txt格式、html格式、json格式、jpg格式等。浏览器通过URL确定资源的位置,但是需要在HTTP请求头中,用Accept和Content-Type字段指定,这两个字段是对资源表现的描述。
-
状态转换:客户端和服务器交互的过程。在这个过程中,一定会有数据和状态的转化,这种转化叫做状态转换。其中,GET表示获取资源,POST表示新建资源,PUT表示更新资源,DELETE表示删除资源。HTTP协议中最常用的就是这四种操作方式。
- RESTful架构:
- 每个URL代表一种资源;
- 客户端和服务器之间,传递这种资源的某种表现层;
- 客户端通过四个http动词,对服务器资源进行操作,实现表现层状态转换。
如何设计符合RESTful风格的API:
一、域名:
将api部署在专用域名下:
http://api.example.com
或者将api放在主域名下:
http://www.example.com/api/
二、版本:
将API的版本号放在url中。
http://www.example.com/app/1.0/info
http://www.example.com/app/1.2/info
三、路径:
路径表示API的具体网址。每个网址代表一种资源。 资源作为网址,网址中不能有动词只能有名词,一般名词要与数据库的表名对应。而且名词要使用复数。
错误示例:
http://www.example.com/getGoods
http://www.example.com/listOrders
正确示例:
#获取单个商品
http://www.example.com/app/goods/1
#获取所有商品
http://www.example.com/app/goods
四、使用标准的HTTP方法:
对于资源的具体操作类型,由HTTP动词表示。 常用的HTTP动词有四个。
GET SELECT :从服务器获取资源。
POST CREATE :在服务器新建资源。
PUT UPDATE :在服务器更新资源。
DELETE DELETE :从服务器删除资源。
示例:
#获取指定商品的信息
GET http://www.example.com/goods/ID
#新建商品的信息
POST http://www.example.com/goods
#更新指定商品的信息
PUT http://www.example.com/goods/ID
#删除指定商品的信息
DELETE http://www.example.com/goods/ID
五、过滤信息:
如果资源数据较多,服务器不能将所有数据一次全部返回给客户端。API应该提供参数,过滤返回结果。 实例:
#指定返回数据的数量
http://www.example.com/goods?limit=10
#指定返回数据的开始位置
http://www.example.com/goods?offset=10
#指定第几页,以及每页数据的数量
http://www.example.com/goods?page=2&per_page=20
六、状态码:
服务器向用户返回的状态码和提示信息,常用的有:
200 OK :服务器成功返回用户请求的数据
201 CREATED :用户新建或修改数据成功。
202 Accepted:表示请求已进入后台排队。
400 INVALID REQUEST :用户发出的请求有错误。
401 Unauthorized :用户没有权限。
403 Forbidden :访问被禁止。
404 NOT FOUND :请求针对的是不存在的记录。
406 Not Acceptable :用户请求的的格式不正确。
500 INTERNAL SERVER ERROR :服务器发生错误。
七、错误信息:
一般来说,服务器返回的错误信息,以键值对的形式返回。
{
error:'Invalid API KEY'
}
八、响应结果:
针对不同结果,服务器向客户端返回的结果应符合以下规范。
#返回商品列表
GET http://www.example.com/goods
#返回单个商品
GET http://www.example.com/goods/cup
#返回新生成的商品
POST http://www.example.com/goods
#返回一个空文档
DELETE http://www.example.com/goods
九、使用链接关联相关的资源:
在返回响应结果时提供链接其他API的方法,使客户端很方便的获取相关联的信息。
十、其他:
服务器返回的数据格式,应该尽量使用JSON,避免使用XML。
python database.py db downgrade 版本号
前后端实现分离的好处:
- 前后端依赖性强,开发效率低,
- 为了代码的复用性,,如果有相似业务逻辑的代码,代码即可复用

浙公网安备 33010602011771号