当前位置: 首页 > 工具软件 > influxdb-orm > 使用案例 >

Flask框架——ORM数据库

董翰墨
2023-12-01


一.通过SQLAlchemy(ORM)操作数据库的流程

1.安装扩展包, pip install flask_sqlalchemy
2.安装数据库驱动, pip install mysqldb / pymysql
3.导⼊SQLAlchemy类
4.配置数据库的链接信息等(可通过报错获取)
5.创建db = SQLAlchemy()对象,关联app
6.编写模型类,继承⾃db.Model
7.编写属性,db.Colomn(类型,约束信息)表示⼀列
8.操作(数据库的增删改查)

二.注意点

1.ORM不能创建数据库,只能创建表
2.默认情况下,⽣成的表名称是类名称的⼩写形式,如果需要指定,设置tablename
3.链接数据库的格式:
<协议名称>://<⽤户名>:<密码>@<ip地址>:<端⼝>/<数据库名>
如果使⽤的是mysqldb驱动,协议名: mysql
如果使⽤的是pymysql驱动,协议名: mysql+pymysql

三.创建数据表类(继承db.Model)

1.设置表名: __tablename__ = 表名(字符串)
2.设置字段:字段名 = db.Column(db.数据类型,约束)
3设置外键:字段名 = db.Column(db.数据类型,db.ForeignKey(关联的表名.字段名))
注意:应设置__repr__方法,返回打印输出的格式,否则按默认方式返回
例:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)

# 指定数据库的链接信息
app.config["SQLALCHEMY_DATABASE_URI"] = "mysql+pymysql://root:zsh123@127.0.0.1:3306/basic12"
# 这个配置将来会被禁用,设置为True或者False可以解除警告信息,建议设置False
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False

db = SQLAlchemy(app)


# 角色模型类(一方)
class Role(db.Model):
    __tablename__ = "roles"
    # db.Colomn()表示模型类的属性,
    # 主键
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)

    def __repr__(self):
        return "<Role:%s,%s>" % (self.id, self.name)


# 用户模型类(多方)
class User(db.Model):
    __tablename__ = "users"
    # db.Colomn()表示模型类的属性,
    # 主键
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)
    email = db.Column(db.String(64), unique=True)   
    password = db.Column(db.String(64), unique=True)

    # 外键,关联的是Role表中的主键id
    role_id = db.Column(db.Integer, db.ForeignKey(Role.id))

    # 如果继承自object的类,使用__str__可以方便的查询对象的输出内容
    # 如果继承自db.Model的类,使用__repr__可以方便的查询对象的输出内容
    def __repr__(self):
        return "<User:%s,%s,%s,%s>" % (self.id, self.name, self.email, self.password)


print(User.query.all())

四.增删改操作

方法描述
db.create_all()创建所有继承⾃db.Model的类
db.drop_all()删除所有继承⾃db.Model的类
db.session.add(obj)添加对象
db.session.add_all([obj1,obj2,…])添加多个对象
db.session.delete(obj)删除对象
模型类.属性名 = xxx修改对象
db.session.commit()提交会话
db.session.rollback()回滚
db.session.remove()移除会话

注意:所有使数据库发生改变的操作都需要提交回话(db.session.commit())才会改变数据库

五.查询数据库

格式:User.query.查询过滤器.查询执行器
1.常用查询过滤器

过滤器说明
filter()把过滤器添加到原查询上,返回一个新查询
filter_by()把等值过滤器添加到原查询上,返回一个新查询
limit使用指定的值限定原查询返回的结果
offset()偏移原查询返回的结果,返回一个新查询
order_by()根据指定条件对原查询结果进行排序,返回一个新查询
group_by()根据指定条件对原查询结果进行分组,返回一个新查询

2.常用执行过滤器

方法说明
all()以列表形式返回查询的所有结果
first()返回查询的第一个结果,如果未查到,返回None
first_or_404()返回查询的第一个结果,如果未查到,返回404
get()返回指定主键对应的行,如不存在,返回None
get_or_404()返回指定主键对应的行,如不存在,返回404
count()返回查询结果的数量
paginate()返回一个Paginate对象,它包含指定范围内的结果

例:

查询id为4的用户[3种方式]
User.query.get(4)
User.query.filter_by(id = 4).first()
User.query.filter(User.id == 4).first()

查询名字结尾字符为g的所有数据[开始/包含]
User.query.filter(User.name.endswith('g')).all()
User.query.filter(User.name.starstswith('g')).all()
User.query.filter(User.name.contains('g')).all()

查询名字不等于wang的所有数据[2种方式]
查询名字和邮箱都以 li 开头的所有数据[2种方式]
User.query.filter(User.name.startswith('li'),User.email.startswith('li')).all()
User.query.filter(and_(User.name.startswith('li'),User.email.startswith('li'))).all()

查询password是 `123456` 或者 `email` 以 `itheima.com` 结尾的所有数据
User.query.filter(or_(User.password == '123456',User.email.endswith('itheima.com'))).all()

查询id为 [1, 3, 5, 7, 9] 的用户列表
User.query.filter(User.id.in_([1,3,5,7,9])).all()
查询name为liu的角色数据
user = User.query.filter(User.name == 'liu').first()
role = Role.query.filter(Role.id == user.role_id).first()

查询所有用户数据,并以邮箱排序
User.query.order_by(User.email).all()
User.query.order_by(User.email.desc()).all()

每页3个,查询第2页的数据
paginate = User.query.paginate(page, per_page,Error_out)
paginate = User.query.paginate(2,3,False)
page: 哪一个页
per_page: 每页多少条数据
Error_out: False 查不到不报错

pages: 共有多少页
items: 当前页数的所有对象
page: 当前页

六.relationship关联

1.一对多(有外键连接的前提):
一对多的数据库关系映射中,多方定义外键,一方定义关系
角色表:

class Role(db.Model):
    __tablename__ = "roles"
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(32))

    # 给Role添加关系属性users, 查询的方式: role.users
    # 给User添加关系属性role, 查询的方式: user.role
    users = db.relationship("User", backref="role", lazy="dynamic")

    # 为了方便查看对象输出内容,重写repr
    def __repr__(self):
        return "<Role:%s,%s>" % (self.id, 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))
    password = db.Column(db.String(32))

    # 关键
    role_id = db.Column(db.Integer, db.ForeignKey(Role.id))

    # 为了方便查看对象输出内容,重写repr
    def __repr__(self):
        return "<User:%s,%s,%s,%s>" % (self.id, self.name, self.email, self.password)

用户表通过外键绑定角色表,从而知道用户的角色,设置relationship关联后,可以直接通过属性访问。

格式: users = db.relationship("User", backref="role", lazy="dynamic")

参数描述
users可以设置任意字符串,设置后users为角色类的一个属性,role.users可直接访问用户表中与role对象相关联的数据(role.users可查看角色为role的用户)
backref设置反向关联属性,role也会成为用户类的一个属性,user.role可直接访问角色表中与user对象相关联的数据(user.role可查看用户user的角色)
lazy使⽤关系属性之后,默认会做⼦查询(不管⽤不⽤的到都会查询), 可以将lazy设置动态查询

默认: lazy = “subquery”,不管⽤不⽤的到都会查询
建议设置: lazy = “dynamic”,只有⽤到了才去查
格式:关联属性 = db.relationship(连接的数据库类名, backref=反向绑定属性)

(1)原始写法:

user = User.query.get(1)
role = Role.query.get(user.role_id)

(2)快速查询方式:

users = db.relationship("User",backref="role")
(设置后可通过users属性直接访问连接的User属性)
user = User.query.get(1)
role = user.role

2.多对多:
多对多需要创建一个中间表,将两张表的主键存储为中间表的外键进行连接。
中间表格式:

表名称 = db.Table( "表名称",字段1,字段2 )

其中一方表:

db.relationship("模型类名",backref="属性",secondary="中间表名")

例:

#学生,课程,中间表
student_course_tb = db.Table(
    "student_course_tb",
    db.Column("student_id",db.Integer,db.ForeignKey("students.id")),
    db.Column("course_id",db.Integer,db.ForeignKey("courses.id"))
)

#编写模型类
#学生模型
class Student(db.Model):
    __tablename__ = "students"
    id = db.Column(db.Integer,primary_key=True)
    name = db.Column(db.String(32))

    #关系属性
    courses = db.relationship("Course",backref="students",secondary="student_course_tb")

    #为了方便,查看对象输出内容
    def __repr__(self):
        return "<Student:%s,%s>"%(self.id,self.name)

#课程模型
class Course(db.Model):
    __tablename__ = "courses"
    id = db.Column(db.Integer,primary_key=True)
    name = db.Column(db.String(32))

    #为了方便,查看对象输出内容
    def __repr__(self):
        return "<Class:%s,%s>"%(self.id,self.name)
Student对象.courses可访问关联的课程,Course对象.students可访问选该课的学生

七.数据库迁移

1.目的:备份表的结构
2.过程:
(1)安装扩展:

pip install flask_migrate
pip install flask_script

(2)导入类:

from flask_migrate import Migrate, MigrateCommand
from flask_script import Manager

Manager:管理app
Migrate:关联app,db
MigrateCommand:用manager添加操作命令使用MigrateCommand进行迁移(manager.add_command(“db”,MigrateCommand))

(3)终端指令:
⽣成迁移⽂件 (⼀次就好)

python xxx.py db init

将模型类,⽣成迁移脚本 (重复执⾏)

python xxx.py db migrate -m '注释'

将迁移脚本更新到数据库 (重复执⾏)

 python xxx.py db upgrade / downgrade [version]

查看所有的版本号

pythoh xxx.py db history

查看当前版本号

python xxx.py db current

3.注意点:
(1)数据库迁移是为了备份表结构,⽽不是数据
(2)如果要备份数据,需要使⽤⼯具, ⽐如: navicat, mysqlworkbench等等⼯具
(3)如果进⾏降级的时候,会丢失数据,所以谨慎操作. 升级不会
例:

from flask import Flask
from flask_migrate import Migrate, MigrateCommand
from flask_script import Manager
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)

# 设置数据库配置信息
app.config["SQLALCHEMY_DATABASE_URI"] = "mysql+pymysql://root:zsh123@localhost:3306/basic12"
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False

# 创建SQLAlchemy对象,关联app
db = SQLAlchemy(app)

# 通过Manager管理app
manager = Manager(app)

# - 使用Migrate,关联app,db
Migrate(app, db)

# 给manager添加操作命令,使用MigrateCommand
manager.add_command("db", MigrateCommand)


# 模型类
class Student(db.Model):
    __tablename__ = "students"
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(32))
    age = db.Column(db.Integer)


@app.route('/')
def hello_world():
    return "helloworld"


if __name__ == '__main__':
    manager.run()

八.蓝图

1.目的: 为模块化开发⽽⽣

2.优点:弱耦合
实际开发中会用很多视图和很多种类的视图,写在一个文件中耦合度太高,所以使用可以使用蓝图分多模块编写代码,然后将每个模块的蓝图对象在app上注册。

3.操作步骤:
(1)导入蓝图:

from flask import Blueprint

(2)创建蓝图对象:

blue = Blueprint("蓝图名称",__name__)

可选参数:

static_folder: 静态⽂件夹
url_prefix: 蓝图资源访问前缀
template_folder: 蓝图模板⽂件夹

(3)使⽤蓝图装饰视图函数

@blue.route("/路径")

(4)将蓝图对象注册到app

app.register_blueprint(blue)
 类似资料: