在Django框架中内部已经提供ORM这样的框架,来实现对象关系映射,方便我们操作数据库。如果想在Flask中也达到这样效果,需要安装一个第三方来支持。
SQLAlchemy是一个关系型数据库框架,它提供了高层的ORM和底层的原生数据库的操作。flask-sqlalchemy是一个简化了SQLAlchemy操作的flask扩展。
安装
pip install flask-sqlalchemy
这里以mysql数据库为例
安装pymysql
pip install pymysql
相关配置
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
import pymysql
pymysql.install_as_MySQLdb()
app = Flask(__name__)
# 设置连接数据库的URL
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:123456@127.0.0.1:3306/db_flask'
# 数据库和模型类同步修改
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
# 查询时会显示原始SQL语句
app.config['SQLALCHEMY_ECHO'] = True
# 管理app
db = SQLAlchemy(app)
字段类型
类型 | 对应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 | 二进制文件 |
约束类型
选项 | 说明 | |
---|---|---|
primary_key | 如果为True,代表表的主键 | |
unique | 如果为True,代表这列不允许出现重复的值 | |
index | 如果为True,为这列创建索引,提高查询效率 | |
nullable | 如果为True,允许有空值,如果为False,不允许有空值 | |
default | 为这列定义默认值 |
关系类型
选项 | 说明 |
---|---|
backref | 在关系的另一模型中添加反向引用 |
primary join | 明确指定两个模型之间使用的联结条件 |
uselist | 如果为False,不使用列表,而使用标量值 |
order_by | 指定关系中记录的排序方式 |
secondary | 指定多对多中记录的排序方式 |
secondary join | 在SQLAlchemy中无法自行决定时,指定多对多关系中的二级联结 |
创建
在Flask-SQLAlchemy中,插入、修改、删除操作,均由数据库会话管理。会话用db.session表示。在准备把数据写入数据库前,要先将数据添加到会话中然后调用commit()方法提交会话。
如果失败还可以回滚:db.rollback(),实现回话提交数据到以前的状态
模型类
"""
相关配置的代码 记得改为你自己的数据库
"""
class Type(db.Model):
__tablename__ = 'tbl_type' # 表的名字 如果不写就以类名命名
id = db.Column(db.Integer,primary_key=True) # 主建
name = db.Column(db.String)
id = db.Column(db.Integer, primary_key=True) # 主键
name = db.Column(db.String(32), unique=True) # 名字
# 数据库中不存在的字段,只是为了查找和反向查找。
# backref:在关系的另一模型中添加反向引用
heros = db.relationship("Hero", backref='type')
# 英雄
class Hero(db.Model):
# 表名
__tablename__ = 'tbl_heros'
# 数据库真正存在的字段
id = db.Column(db.Integer, primary_key=True) # 主键
name = db.Column(db.String(64), unique=True) # 名字
gender = db.Column(db.String(64)) # 性别
# 外键 一个射手对应很多英雄
type_id = db.Column(db.Integer, db.ForeignKey("tbl_types.id"))
if __name__ == "__main__":
db.create_all() # 创建表
type1 = Type(name='射手')
db.session.add(type1) # 添加到会话
db.session.commit() # 提交
type2 = Type(name='坦克')
db.session.add(type2)
db.session.commit()
type3 = Type(name='法师')
type4 = Type(name='刺客')
db.session.add_all([type3, type4]) # 添加多个
db.session.commit()
hero1 = Hero(name='后羿', gender='男', type_id=type1.id)
hero2 = Hero(name='程咬金', gender='男', type_id=type2.id)
hero3 = Hero(name='王昭君', gender='女', type_id=type3.id)
hero4 = Hero(name='安琪拉', gender='女', type_id=type3.id)
hero5 = Hero(name='兰陵王', gender='男', type_id=type4.id)
db.session.add_all([hero1, hero2, hero3, hero4, hero5]) # 添加多个
db.session.commit()
到你数据库查一下
查询
Flask-SQLAlchemy中常用过滤器:
过滤器 | 说明 | |
---|---|---|
filter() | 把过滤器添加到原查询上,返回一个新查询 | |
filter_by() | 把等值过滤器添加到原查询上,返回一个新查询 | |
limit() | 使用指定的值限定原查询返回的结果 | |
offset() | 偏移原查询返回的结果,返回一个新查询 | |
order_by() | 根据指定条件对原查询结果进行排序,返回一个新查询 | |
group_by() | 根据指定条件对原查询结果进行分组,返回一个新查询 |
Flask-SQLAlchemy中常用执行器:
方法 | 说明 | |
---|---|---|
all() | 以列表形式返回查询的所有结果 | |
first() | 返回查询的第一个结果,如果未查到,返回None | |
first_or_404() | 返回查询的第一个结果,如果未查到,返回404 | |
get() | 返回指定主键对应的行,如不存在,返回None | |
get_or_404() | 返回指定主键对应的行,如不存在,返回404 | |
count() | 返回查询结果的数量 | |
paginate() | 返回一个Paginate对象,它包含指定范围内的结果 |
这里举几个例子:
查全部分类:
Type.query.all()
根据分类过滤:
Type.query.filter_by(id = 1)
注意:
filter和 filter_by 的区别:
Type.query.filter(类.字段 == 条件) Type.query.filter_by(字段 = 条件)
逻辑与
Hero.query.filter_by(name='王昭君',type_id=3).first()
逻辑或
from sqlalchemy import or_
Hero.query.filter(or_(Hero.name.endswith('君'),Hero.type_id==3)).all()
排序
降序查询
Hero.query.order_by(Hero.id.desc()).all()
升序查询
Hero.query.order_by(Hero.id.asc()).all()
各种查询方法还有很多,大家可以去google或是百度
更新
- 第一种
hero = Hero.query.get(1)
hero.name = '伽罗'
db.session.add(hero)
db.session.commit() - 第二种
Hero.query.filter_by(id=1).update({"name":"虞姬","gender":"女"})
db.session.commit()
删除
hero = Hero.query.get(4)
db.session.delete(hero)
db.session.commit()
模型迁移
在Django框架开发过程中,我们对数据库字段添加或删除,直接修改模型类,然后进行迁移可以了,非常方便。我们也想让Flask框架支持这样的操作,就需要使用Flask-Migrate扩展,来实现数据迁移。并且集成到Flask-Script中,所有操作通过命令就能完成。
安装插件
pip install Flask-Script
pip install flask-migrate
使用
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
import pymysql
from flask_migrate import Migrate, MigrateCommand
from flask_script import Manager
pymysql.install_as_MySQLdb()
app = Flask(__name__)
# 通过脚本管理flask程序
manager = Manager(app)
"""
相关配置
"""
db = SQLAlchemy(app)
# 创建数据库迁移对象
Migrate(app, db)
# 向脚步管理添加数据库迁移命令 db指命令的别名
manager.add_command('db', MigrateCommand)
"""
模型代码
"""
初始化 只是在每个项目第一次生成迁移用到 以后就不用了
python3 app.py db init
app.py >> 你自己的文件名 db >> 上面指定的命令别名
在你的项目文件下 多出 migrations 的文件
生成迁移文件
python app.py db migrate -m 'first create'
提示:
INFO [alembic.runtime.migration] Context impl MySQLImpl.
INFO [alembic.runtime.migration] Will assume non-transactional DDL.
INFO [alembic.env] No changes in schema detected.
提交:
python flask_migrate_db.py db upgrade
ok 你的数据库已经有了数据
回退:
回退数据库时,需要指定回退版本号,由于版本号是随机字符串,为避免出错,建议先使用python flask_migrate_db.py db history命令查看历史版本的具体版本号,然后复制具体版本号执行回退。
python flask_migrate_db.py db downgrade base
python flask_migrate_db.py db downgrade 4cee71e47df3
4cee71e47df3 >>版本号
模型关系
在数据库中,我们知道数据关系大概有如下几种:一对一、一对多、多对多、自关联等。我们模型已经描述过了一对多,那么下面我们在用模型把其它关系也写出来。
一对一
# 文章模型
class Article(db.Model):
# 表名
__tablename__ = 'tbl_article'
# 数据库真正存在的字段
id = db.Column(db.Integer, primary_key=True) # 主键
title = db.Column(db.String(128), unique=True) # 名字
# 方便查找,数据并不存在的字段
content = db.relationship('Acontent', backref='article', uselist=False) #一对一需要把uselist设置为False
# 内容模型
class Acontent(db.Model):
# 表名
__tablename__ = 'tbl_acontent'
# 数据库真正存在的字段
id = db.Column(db.Integer, primary_key=True) # 主键
content = db.Column(db.Text(4000)) # 名字
一对多
# 分类模型
class Category(db.Model):
# 表名
__tablename__ = 'tbl_category'
# 数据库真正存在的字段
id = db.Column(db.Integer, primary_key=True) # 主键
name = db.Column(db.String(32), unique=True) # 名字
# 方便查找,数据并不存在的字段
article = db.relationship('Article', backref='category')
# 文章模型
class Article(db.Model):
# 表名
__tablename__ = 'tbl_article'
# 数据库真正存在的字段
id = db.Column(db.Integer, primary_key=True) # 主键
title = db.Column(db.String(128), unique=True) # 名字
category_id = db.Column(db.Integer, db.ForeignKey('tbl_category.id')) # 分类id
多对多
# 辅助表
tbl_tags = db.Table('tbl_tags',
db.Column('tag_id', db.Integer, db.ForeignKey('tbl_tag.id')),
db.Column('article_id', db.Integer, db.ForeignKey('tbl_article.id'))
)
# 标签模型
class Tag(db.Model):
# 表名
__tablename__ = 'tbl_tag'
# 数据库真正存在的字段
id = db.Column(db.Integer, primary_key=True) # 主键
name = db.Column(db.String(32), unique=True) # 名字
# 文章模型
class Article(db.Model):
# 表名
__tablename__ = 'tbl_article'
# 数据库真正存在的字段
id = db.Column(db.Integer, primary_key=True) # 主键
title = db.Column(db.String(128), unique=True) # 名字
category_id = db.Column(db.Integer, db.ForeignKey('tbl_category.id')) # 分类id
# 方便查找,数据并不存在的字段
content = db.relationship('Acontent', backref='article')
tags = db.relationship('Tag', secondary=tbl_tags, backref='articles')
# secondary=tbl_tags, tbl_tags->辅助表的名字 注意!!!
自关联
# 地区模型
class Area(db.Model):
# 表名
__tablename__ = "tbl_area"
# 数据库真正存在的字段
id = db.Column(db.Integer, primary_key=True) # 主键
name = db.Column(db.Text, nullable=False) # 地区名字
parent_id = db.Column(db.Integer, db.ForeignKey("tbl_area.id")) # 父评论id
# 方便查找,数据并不存在的字段
parent = db.relationship("Area", remote_side=[id]) # 自关联需要加remote_side
flask 数据库的有关操作就到此结束了,有什么问题可以给我留言,看到会回复大家的
喜欢就点播关注吧-_-!