三、Flask模型基础 ORM
1. Flask模型Model
Flask默认并没有提供任何数据库操作的API
我们可以选择任何适合自己项目的数据库来使用Flask中可以自己的选择用原生语句实现功能,也可以选择ORM(SQLAlchemy,MongoEngine) 原生SQL缺点
代码利用率低,条件复杂代码语句越长,有很多相似语句一些SQL是在业务逻辑中拼出来的,修改需要了解业务逻辑直接写SQL容易忽视SQL问题
2. ORM
Flask通过Model操作数据库,不管你数据库的类型是MySql或者Sqlite,Flask自动帮你生成相应数据库类型的SQL语句,所以不需要关注SQL语句和类型,对数据的操作F lask帮我们自动完成。只要会写Model就可以了, Flask使用对象关系映射(object Relational Mapping,简称ORM)框架去操控数据库。 ORM(object Relational Mapping)对象关系映射,是一种程序技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换。 将对对象的操作转换为原生SQL 优点
易用性,可以有效减少重复SQL性能损耗少设计灵活,可以轻松实现复杂查询移植性好
3. Flask的ORM
I. ORM
Flask使用Python自带的ORM: SQLAlchemy针对于flask的支持,安装插件flask- sqlalchemypip install flask-sqlalchemy
II. 连接SQLite
SQLite连接的URI:
DB_URI = sqlite:///sqlite3.db
III. 连接MySQL
USERNAME= 'root'
PASSWORD= 'admin123'
HOSTNAME = 'localhost'
PORT = '3306'
DATABASE = 'HelloFlask'
# 格式: mysql+pymysql://USERNAME@PASSWORDHOSTNANE:PORT/DATABASE
# 配置URL
DB_URI='mysql+pyaysql://{}:{}@{}:{}/{}'.format(
USERNAME,
PASSWORD,
HOSTNAME,
PORT,
DATABASE
)
IV. 在Flask中使用ORM
模型相关包安装 #安装flask-sqlalchemy (用 于ORM)
pip install flask-sqlalchemy
#安装flask-migrate (用于数据迁移)
pip install flask-migrate
#安装pymysql (MySQL驱动)
pip install pymysql
在App目录下新增exts.py用于插件管理 # exts.py 插件管理
# 1.导包
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
# 2.初始化
db = SQLAlchemy() # ORM
migrate = Migrate() # 数据迁移
# 3.和app对象绑定
def init_exts(app):
db.init_app(app=app)
migrate.init_app(app=app, db=db)
在models.py中构建模型 # model.py : 模型,数据库
from .exts import db
# 模型 => 数据库
# 类 => 表结构
# 类属性 => 表字段
# 一个类对象 => 表的一行数据
# 模型Model: 类
# 必须继承db.Model
class User(db.Model):
# 表名
__tablename__ = 'tb_user'
# 定义表字段
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.String(30), unique=True, index=True)
age = db.Column(db.Integer, default=1)
sex = db.Column(db.Boolean, default=True)
salary = db.Column(db.Float, default=100000.0, nullable=False)
salary2 = db.Column(db.Float, default=100000.0, nullable=False)
# db.Column : 表示字段
# db.Integer : 表示整数
# primary_key=True : 主键
# autoincrement=True :
# db.String(30) : varchar(30) 可变字符串
# unique=True : 唯一约束
# defalut=1 : 默认值为1
# nullable=False : 不允许为空
在App目录下的__init__.py,配置数据库相关参数,初始化数据库连接 # __init__.py : 初始化文件,创建Flask应用
from flask import Flask
from .views import blue
from .exts import init_exts
def create_app():
app = Flask(__name__)
# 注册蓝图
app.register_blueprint(blueprint=blue)
# 配置数据库,SQLite数据库连接不需要额外驱动,也不需要用户名和密码
db_uri = 'sqlite:///sqlite3.db'
app.config['SQLALCHEMY_DATABASE_URI'] = db_uri # 配置连接的数据库路径
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False # 禁止对象追踪修改
# 初始化插件
init_exts(app)
return app
数据迁移 数据迁移详细步骤:
1.安装好数据迁移的包flask-sqlalchemy和flask-migrate 2.在exts.py中初始化Migrate和sQLAlchemy 3.在models中定义好模型 4.在views.py中一定要导入models模块 from .models import *
5.配置好数据库(sqlite3或MysQL) 6.执行数据迁移命令:
6.1 先在cmd或Terminal进入项目目录(app.py所在目录)∶6.2 然后输入命令:
flask db init 创建迁移文件夹migrates,只调用一次flask db migrate生成迁移文件,自动检测models.py的模型flask db upgrade执行迁移文件中的升级(执行模型的建表,表字段更新等等)flask db downgrade损行迁移文件中的降级 7.查看数据库内容,第一次使用需要下载数据库驱动,自己download下载
4. 模型操作
I.创建模型
如果无__tablename__属性,表名为对应模型小写,即Person -> person
class Person(db.Model):
__tablename__ = 'person'
id = db.column(db.Integer, primary_key=True)
nane = db.column(db.string(16), unique=True)
字段类型
类型名Python模型说明IntegerInt普通整数,一般是32位SmallintegerInt取值范围小的整数一般是16位BigintegerInt或long不限制精度的整数Floatfloat浮点数Numericdecimal.Decimal定点数Stringstr变长字符串Textstr长字符串,对较长或不限长字符串做了优化Unicodeunicode变长Unicode字符串Unicodeunicode变长Unicode字符串,对较长或不限长字符串做了优化Booleanbool布尔值Datedatetime.date日期Timedatetime.time时间DateTimedatetime.datetime日期和时间Intervaldatetime.timedelta时间间隔LargeBinarystr二进制文件 常见约束
选项名说明primary_key如果设为True ,这列就是表的主键unique如果设为True ,这列不允许出现重复的值index如果设为True ,为这列创建索引提升查询效率nullable如果设为True ,这列允许使用空值:如果设为False ,这列不允许使用空值default为这列定义默认值
II. 单表操作
增加数据 # 增:添加数据
@blue.route('/useradd/')
def user_add():
# 一次添加一条数据
# u = User()
# u.name = 'kun'
# u.age = 25
# db.session.add(u) # 将u对象添加到session中
# db.session.commit() # 同步到数据库中
# 一次添加多条数据
users = []
for i in range(10, 30):
u = User()
u.name = '小冰' + str(i)
u.age = i
users.append(u)
try:
db.session.add_all(users)
db.session.commit() # 事务提交
except Exception as e:
db.session.rollback() # 回滚
db.session.flush() # 清空缓存
return 'fail' + str(e)
return 'success!'
删除数据 # 删:删除数据
# 找到要删除的数据,然后删除
@blue.route('/userdel/')
def user_del():
u = User.query.first() # 查询第一条数据
db.session.delete(u)
db.session.commit()
return 'success!
修改数据 # 改:修改数据
@blue.route('/userupdate/')
def user_update():
u = User.query.first()
u.age = 1000
db.session.commit()
return 'success!'
查询数据
all():返回所有数据,返回列表 users = User.query.all()
print(users, type(users)) # [...]
print(User.query, type(User.query)) #
filter() : 过滤,类似SQL中的where users = User.query.filter()
print(users, type(users)) # 查询集 可以继续.filter()...
print(list(users))
get(key): 查询到对应主键的数据对象,主键不存在返回None user = User.query.get(8)
print(user, type(user)) # 小冰15
print(user.name, user.age) # 获取数据的属性值
user.age = 15
db.session.commit()
filter() : 类似SQL中的where,filter_by() : 用于等值操作中的过滤。注意==和= # filter() : 类似SQL中的where
# filter_by() : 用于等值操作中的过滤
users = User.query.filter(User.age == 21)
users = User.query.filter_by(age=21)
users = User.query.filter(User.age > 21) # 可以用于非等值操作
print(list(users), type(users))
first() : 第一条数据,first_or_404() : 第一条数据,如果不存在则抛出404错误 user = User.query.first()
user = User.query.filter_by(age=100).first_or_404()
print(user)
count() : 统计查询集中的数据条数 count = User.query.count()
users = User.query.filter()
print(count, users.count())
limit() : 查询前几条数据,offset() : 跳过前几条 (可以用于手动分页) users = User.query.offset(3).limit(4)
print(list(users))
order_by() : 排序,默认升序(asc),降序使用desc users = User.query.order_by(desc('age'))
print(list(users))
逻辑运算:and_():且,or_():或,not_:非、取反, 默认是且操作 users = User.query.filter(User.age > 20, User.age < 25)
users = User.query.filter(and_(User.age > 20, User.age < 25)) # 默认就是且操作and_
users = User.query.filter(or_(User.age > 25, User.age < 20))
users = User.query.filter(not_(or_(User.age > 25, User.age < 20)))
print(list(users))
查询属性 User(类)属性上的方法 # contains(x) : 模糊查询,包含x子串,类似SQL的like
users = User.query.filter(User.name.contains('3'))
# in_([...]) : 其中之一
users = User.query.filter(User.age.in_([10, 25, 30, 40, 50]))
# startswith(x): 以x子串开头,endswith(x): 以x子串结尾
users = User.query.filter(User.name.startswith('小'))
users = User.query.filter(User.name.endswith('9'))
# __gt__ : 大于
# __ge__ : 大于等于
# __lt__ : 小于
# __le__ : 等于
users = User.query.filter(User.age.__gt__(20))
print(list(users))
分页(翻页查询)
手动翻页 # 1. 手动翻页
# offset().limit()
# 数据 1-20
# 页码:page = 1
# 每页显示数量:per_page = 5
# 总数:total=20
# 总页数:pages = 4
# page=1 : 1 2 3 4 5 => offset(0*5).limit(5)
# page=2 : 6 7 8 9 10 => offset(1*5).limit(5)
# ...
# page=n ((n-1)*5<=total) => offset((n-1)*5).limit(5)
# page=n ((n-1)*5>total) => offset((pages-1)*5).limit(5)
paginate对象实现分页 @blue.route('/userpaginate/')
def user_paginate():
# 页码:默认显示第一页
page = int(request.args.get('page', default=1))
# per_page: 每页显示数量
per_page = int(request.args.get('per_page', 5))
# print(page,type(page))
# print(per_page,type(per_page))
# paginate()
p = User.query.paginate(page=page, per_page=per_page, error_out=False)
# paginate对象的属性:
# items:返回当前页的内容列表
print(p.items)
# has_next:是否还有下一页
print(p.has_next)
# has_prev:是否还有上一页
print(p.has_prev)
# next(error_out=False):返回下一页的Pagination对象
print(p.next(error_out=False).items)
# prev(error out=Faise):返回上一页的Pagination对象
print(p.prev(error_out=False).items)
# page:当前页的页码(从1开始))
print(p.page)
# pages:总页数
print(p.pages)
# per_page:每页显示的数量
print(p.per_page)
# prev_num: 上一页页码数
print(p.prev_num)
# next_num: 下一页页码数
print(p.next_num)
# total:查询返回的记录总数
print(p.total)
return render_template('paginate.html',p=p)
paginate对象属性及方法:
属性说明items返回当前也的内容列表has_prev是否有上一页has_next是否有下一页next()返回下一页的paginate对象prev()返回上一页的paginate对象page当前的页码(1开始)pages总页数per_page每页显示数量prev_num上一页页码数next_num下一页页码数total查询数据(记录)总数 III. 多表操作 一对多
一个班级对应多个学生
from .exts import db
# 多表关系
# ------------ 一对多 ------------ #
# 班级:学生 = 1:N
# 班级表
class Grade(db.Model):
# 表名
__tablename__ = 'grade' # 表名
# 定义表字段
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.String(30), unique=True)
# 建立关联
# 第一个参数:关联的模型名(表)
# 第二个参数:反向引用的名称(对应的__tablename__),grade对象,让student去反过来得到grade对象的名称
# 第三个参数:懒加载
# 这里的students不是字段
students = db.relationship('Student', backref='grade', lazy=True)
class Student(db.Model):
__tablename__ = 'student'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.String(30), unique=True)
age = db.Column(db.Integer)
# 外键
gradeid = db.Column(db.Integer, db.ForeignKey(Grade.id))
注意:
外键写在多的一方,在学生类(Student)中写 gradeid = db.Column(db.Integer, db.ForeignKey(Grade.id))并且要班级类(Grade)中建立联系 students = db.relationship('Student', backref='grade', lazy=True) 多对多
用户收藏电影 N:M,本质是多个一对多
# ------------ 多对多 ------------ #
# 用户收藏电影
# 用户 :电影 = N:M
# 中间表(写在最上面)
collect = db.Table(
'collects', # 表名
db.Column('user_id', db.Integer, db.ForeignKey('user.id'), primary_key=True),
db.Column('movie_id', db.Integer, db.ForeignKey('movie.id'), primary_key=True)
)
# 用户表
class UserModel(db.Model):
__tablename__ = 'user'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.String(30))
age = db.Column(db.Integer)
# 电影表
class MovieModel(db.Model):
__tablename__ = 'movie'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.String(30))
# 关联属性 users
# secondary=collect : 设置中间表
users = db.relationship('UserModel', backref='movies', lazy='dynamic', secondary=collect)
# lazy属性;
# 懒加载,可以延迟在使用关联属性的时候才建立关联
# lazy = 'dynamic':会返回一个query对象(查询集),可以继续使用其他查询方法,如all().
# lazy = 'select ':首次访问到属性的时候,就会全部加载该属性的数据.
# lazy = 'joined " :在对关联的两个表进行join操作,从而获取到所有相关的对象
# lazy=True:返回一个可用的列表对象,同select
注意:
使用db.table()创建中间表
第一个参数字符串表示中间表的名字后面的参数列表:db.Column('tablename_id', db.Integer, db.ForeignKey('tablename.id'), primary_key=True)表示要绑定的外键 在任意一个非中间表建立联系(relationship) users = db.relationship('UserModel', backref='movies', lazy='dynamic', secondary=collect)
users:MovieModel中的属性,用来记录该电影被哪些用户所收藏 db.relationship:
第一个参数:字符串,写要关联的类第二个参数:backref=movies,反向引用另外模型类(表),值填入另外模型类的__tablename__的值即可第三个参数:懒加载,可以延迟在使用关联属性的时候才建立关联
lazy = 'dynamic':会返回一个query对象(查询集),可以继续使用其他查询方法,如all().lazy = 'select':首次访问到属性的时候,就会全部加载该属性的数据.lazy = 'joined' :在对关联的两个表进行join操作,从而获取到所有相关的对象lazy=True:返回一个可用的列表对象,同select
文章来源
发表评论