【Flask从入门到精通:第九课:数据库基本操作、数据表操作以及数据操作】

数据库操作

数据库驱动(drivers)模块:pymysql、MySQLDB

数据库基本操作

  • 在SQLAlchemy中,添加、修改、删除操作,均由数据库会话(sessionSM)管理。
    • 会话用 db.session 表示。在准备把数据写入数据库前,要先将数据添加到会话中然后调用 db.commit() 方法提交会话。
  • 在SQLAlchemy 中,查询操作是通过 query 对象操作数据。
    • 最基本的查询是返回表中所有数据,也可以通过filter过滤器进行更精确的数据库查询。

模型类定义

我们后面会把模型创建到单独的文件中,但是现在我们先把模型类写在main.py文件中。

python 复制代码
from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
class Config(object):
    DEBUG = True
    # 数据库链接配置 = 数据库名称://登录账号:登录密码@数据库主机IP:数据库访问端口/数据库名称?charset=编码类型
    SQLALCHEMY_DATABASE_URI = "mysql://root:123@127.0.0.1:3306/flaskdemo?charset=utf8mb4"
    # 动态追踪修改设置,如未设置只会提示警告
    SQLALCHEMY_TRACK_MODIFICATIONS = False
    # ORM运行时会显示ORM生成的原始SQL语句[调试]
    SQLALCHEMY_ECHO = True

app.config.from_object(Config)


"""模型类定义"""
db = SQLAlchemy(app=app)
# 等同于
# db = SQLAlchemy()
# db.init_app(app) # 加载配置并完成初始化过程

class Student(db.Model):
    """学生信息模型"""
    # 声明与当前模型绑定的数据表名称
    __tablename__ = "tb_student"
    """
    # 企业中,往往大部分的公司会出现以下2种不同的数据库开发情况。
    # 1. 企业中有DBA,DBA会提前创建数据库和项目中具体业务的数据表。
         也就是说我们不需要自己手动建库建表,只需要根据数据库表结构,使用python声明对应的模型与之匹配,就可以操作数据库了。
    # 2. 企业没有DBA,比较坑爹:
    #    2.1 开发人员,自己手撸SQL语句,手动建库建表。
    #    2.2 开发人员,编写模型,使用数据迁移,手动建库和数据迁移建表。
    
    # 原生SQL语句
    create table db_student(
      id int primary key auto_increment comment "主键",
      name varchar(15) comment "姓名",
      age smallint comment "年龄",
      sex tinyint default 1 comment "性别",
      email varchar(128) comment "邮箱地址",
      money NUMERIC(10,2) default 0.0 comment "钱包",
      key (name),
      unique key (email)
    );
    # 字段根据SQL语句来声明
    """
    id = db.Column(db.Integer, primary_key=True,comment="主键")
    name = db.Column(db.String(15), index=True, comment="姓名")
    age = db.Column(db.SmallInteger, comment="年龄")
    sex = db.Column(db.Boolean, default=True, comment="性别")
    email = db.Column(db.String(128), unique=True, comment="邮箱地址")
    money = db.Column(db.Numeric(10,2), default=0.0, comment="钱包")

    def __repr__(self): # 相当于django的__str__
        return f"{self.name}<{self.__class__.__name__}>"


# 所有的模型必须直接或间接继承于db.Model
class Course(db.Model):
    """课程数据模型"""
    __tablename__ = "db_course"
    """
    # 原生SQL语句
    create table db_course (
        id int primary key auto_increment comment "主键",
        name varchar(64) comment "课程",
        price NUMERIC(7,2) comment "价格",
        unique (name)
    );
    # 字段根据SQL语句来声明
    """
    id = db.Column(db.Integer, primary_key=True, comment="主键")
    name = db.Column(db.String(64), unique=True, comment="课程")
    price = db.Column(db.Numeric(7, 2), comment="价格")
    # repr()方法类似于django的__str__,用于打印模型对象时显示的字符串信息
    def __repr__(self):
        return f"{self.name}<{self.__class__.__name__}>"

class Teacher(db.Model):
    """老师数据模型"""
    __tablename__ = "db_teacher"
    """
    # 原生SQL语句
    create table db_teacher (
        id int primary key auto_increment comment "主键",
        name varchar(64) comment "姓名",
        option enum("讲师", "助教", "班主任") comment "职位",
        unique (`name`)
    );
    # 字段根据SQL语句来声明
    """
    id = db.Column(db.Integer, primary_key=True, comment="主键")
    name = db.Column(db.String(64), unique=True, comment="姓名")
    option = db.Column(db.Enum("讲师", "助教", "班主任"), default="讲师")

    def __repr__(self):
        return f"{self.name}<{self.__class__.__name__}>"

@app.route("/")
def index():
    return "ok"

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

数据表操作

创建和删除表

创建表

python 复制代码
# 在视图内调用:
@app.route("/create")
def create_table():
    db.create_all() # 为项目中被识别的所有模型创建数据表
    return "ok"


# 在视图以外的地方调用:
	with app.app_context():
        # create_all()方法执行的时候,需要放在模型的后面
        # 检测数据库中是否存在和模型匹配的数据表。
        # 如果没有,则根据模型转换的建表语句进行建表。
        # 如果找到,则不会进行额外处理
        db.create_all()

删除表

python 复制代码
# 在视图内调用:
@app.route("/drop")
def drop_table():
    db.drop_all()   # 为项目中被识别的所有模型删除数据表
    return "ok"


# 在视图以外的地方调用:
    with app.app_context():
        db.drop_all()  # 慎用,很给力的!!这表示删除数据库中所有模型对应的表。

代码:

python 复制代码
from flask import Flask, render_template
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()
app = Flask(__name__, template_folder="templates", static_folder="static")

# 配置
app.config.update({
    "DEBUG": True,
    "SQLALCHEMY_DATABASE_URI": "mysql://root:123@127.0.0.1:3306/flaskdemo?charset=utf8mb4",
    # 如果使用pymysql,则需要在连接时指定pymysql
    # "SQLALCHEMY_DATABASE_URI": "mysql+pymysql://root:123@127.0.0.1:3306/flaskdemo?charset=utf8mb4"
    # 动态追踪修改设置,如未设置只会提示警告,设置False即可
    "SQLALCHEMY_TRACK_MODIFICATIONS": False,
    # ORM执行SQL查询时是哦否显示原始SQL语句,debug模式下可以开启
    "SQLALCHEMY_ECHO": True,
})

db.init_app(app)



class Student(db.Model):
    """学生管理"""
    __tablename__ = "db_student" # 表名
    # __abstract__ = True        # 抽象模型,数据迁移/建表的时候,不会认为这是一个模型,也就不会建表,往往用于设置公共模型,保存公共字段
    """
        # 企业中,往往大部分的公司会出现以下2种不同的数据库开发情况。
        # 1. 企业中有DBA,DBA会提前创建数据库和项目中具体业务的数据表。
             也就是说我们不需要自己手动建库建表,只需要根据数据库表结构,使用python声明对应的模型与之匹配,就可以操作数据库了。
        # 2. 企业没有DBA,比较坑爹:
        #    2.1 开发人员,自己手撸SQL语句,手动建库建表。
        #    2.2 开发人员,编写模型,使用数据迁移或者ORM提供建表方法,手动建库和数据迁移建表。
    
        # 原生SQL语句
        create table db_student(
          id int primary key auto_increment comment "主键",
          name varchar(15) comment "姓名",
          age smallint comment "年龄",
          sex tinyint(1) default 1 comment "性别",
          email varchar(255) comment "邮箱地址",
          money NUMERIC(10,2) default 0.0 comment "钱包",
          key (name),
          unique key (email)
        );
        # 字段根据SQL语句来声明
    """
    # 属性名 = db.Column(字段类型, 字段列约束选项)
    # 如果SQL语句中的字段名在python中是关键字/保留字,则建议改写绑定字段名
    # 属性名 = db.Column("字段名", 字段类型, 字段列约束选项)
    id = db.Column("student_id", db.Integer, primary_key=True, comment="主键")
    name = db.Column(db.String(15), index=True, comment="姓名")
    age = db.Column(db.SmallInteger, comment="年龄")
    sex = db.Column(db.SmallInteger, comment="性别")
    email = db.Column(db.String(255), unique=True, comment="邮箱地址")
    money = db.Column(db.Numeric(10,2), default=0.0, comment="钱包")

    # repr()方法类似于django的__str__,用于打印模型对象时显示的字符串信息
    def __repr__(self):
        return f"{self.name}<{self.__class__.__name__}>"

# 所有的模型必须直接或间接继承于db.Model
class Course(db.Model):
    """课程数据模型"""
    __tablename__ = "db_course"
    """
    # 原生SQL语句
    create table db_course (
        id int primary key auto_increment comment "主键",
        name varchar(64) comment "课程",
        price NUMERIC(7,2) comment "价格",
        unique (name)
    );
    # 字段根据SQL语句来声明
    """
    id = db.Column(db.Integer, primary_key=True, comment="主键")
    name = db.Column(db.String(64), unique=True, comment="课程")
    price = db.Column(db.Numeric(7, 2), comment="价格")
    # repr()方法类似于django的__str__,用于打印模型对象时显示的字符串信息
    def __repr__(self):
        return f"{self.name}<{self.__class__.__name__}>"

class Teacher(db.Model):
    """老师数据模型"""
    __tablename__ = "db_teacher"
    """
    # 原生SQL语句
    create table db_teacher (
        id int primary key auto_increment comment "主键",
        name varchar(64) comment "姓名",
        option enum("讲师", "助教", "班主任") comment "职位",
        unique (`name`)
    );
    # 字段根据SQL语句来声明
    """
    id = db.Column(db.Integer, primary_key=True, comment="主键")
    name = db.Column(db.String(64), unique=True, comment="姓名")
    option = db.Column(db.Enum("讲师", "助教", "班主任"), default="讲师")

    def __repr__(self):
        return f"{self.name}<{self.__class__.__name__}>"


@app.route("/")
def index():
    title = "网站首页"
    return render_template("index.html", **locals())


@app.route("/create")
def create_table():
    db.create_all() # 为项目中被识别的所有模型创建数据表
    return "ok"


@app.route("/drop")
def drop_table():
    db.drop_all()   # 为项目中被识别的所有模型删除数据表
    return "ok"

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

数据操作

添加一条数据

python 复制代码
# 添加一条数据
student = Student(name="小明", age=17, email="xiaoming@qq.com", money=100) # 实例化模型对象
db.session.add(student) # 把模型对象添加数据库session会话对象中。db.session是SQLAlchemy中内置的会话管理对象sessionSM的成员
db.session.commit()     # 提交会话

# 再次插入一条数据
student2 = Student(name='小红', sex=False, age=13, email="16565666@qq.com", money=600)
db.session.add(student2)
db.session.commit()     # 提交会话

一次插入多条数据

python 复制代码
# 1. 先实例化要创建的模型对象
student = Student(name="小红", age=17, email="xiaohong@qq.com", money=200)
# 2. 把实例对象添加到连接会话
db.session.add(student)
# 1. 先实例化要创建的模型对象
student = Student(name="小花", age=16, email="xiaohua@qq.com", money=200)
# 2. 把实例对象添加到连接会话
db.session.add(student)
# 3. 只需要在结束的时候提交事务即可
db.session.commit()
python 复制代码
# 1. 先创建的列表要添加的实例化模型对象列表
student_list = [
    Student(name='wang', email='wang@163.com', age=20),
    Student(name='zhang', email='zhang@189.com', age=21),
    Student(name='chen', email='chen@126.com', age=19),
    Student(name='zhou', email='zhou@163.com', age=18),
    Student(name='tang', email='tang@163.com', age=16),
    Student(name='wu', email='wu@gmail.com', age=20),
    Student(name='qian', email='qian@gmail.com', age=21),
    Student(name='liu', email='liu@163.com', age=21),
    Student(name='li', email='li@163.com', age=18),
    Student(name='sun', email='sun@163.com', age=17),
]

# 2. 一次性添加到连接会话中
db.session.add_all(student_list)
db.session.commit()

删除数据

python 复制代码
# 方法1[先查询后删除,2条语句完成删除操作]
# 先查询出来
student = Student.query.first()
print(student)
# 再进行删除
db.session.delete(student)
db.session.commit()

# 方法2【1条语句完成删除操作,性能更好更高效】     
# 类似乐观锁,在数据改动时添加条件并判断条件成立以后才进行数据操作,这种用法就是乐观锁
Student.query.filter(Student.id == 5).delete()
db.session.commit()


"""
悲观锁,是属于数据库中的一种锁机制,但是乐观锁并非真正的数据库锁。
2种锁都是数据库在应对并发操作时,防止出现资源抢夺的,基于不同人生观所实现2种解决方案。
悲观锁的基本使用:
    >>> 数据库终端开始

    begin;  -- 开启事务
    select * from db_student where student_id = 5 for update; -- 添加一把更新锁【悲观锁】
    ....    -- 在事务提交之前,任何第三方连接都不能修改 student_id = 5这条数据 
    commit; -- 提交事务

    <<< 数据库终端开始

悲观锁的问题:
1. 提前锁定数据,形成串行化,形成阻塞,不利于性能发挥,不适用高并发场景。
2. 悲观锁只能保证数据的一致性,不能保证脏数据的出现

乐观锁的出现就是为了解决悲观锁的问题。
举例:双11活动,商城里面id=5的商品的库存=10了,现在我们要基于乐观锁和悲观锁来解决下单过程中,出现的资源抢夺现象,避免出现超卖(商品数量不能为负数)。

乐观锁:
---> begin;  开启事务
---> 先查看库存,记录当前库存 num=10
---> 进行下单操作,买6件
---> 付款
---> 扣除库存 update goods set num=num-6 where num=10 and id=5;  # 增加更新条件,判断库存是否还是原来
---> 如果执行成功,则表示没有人抢,购买成功
     如果执行事变,则表示已经有人先抢购
---> commit;

悲观锁:
---> begin; 开启事务
---> 先给id=5的数据,加锁
     select * from goods where id=5 for update;
---> 进行下单操作,买6件
---> 付款
---> 扣除库存  update goods set num=num-6 where id=5
---> 执行成功解锁
---- commit;  提交事务
"""

更新数据

python 复制代码
from flask import Flask, render_template
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()
app = Flask(__name__, template_folder="templates", static_folder="static")

# 配置
app.config.update({
    "DEBUG": True,
    "SQLALCHEMY_DATABASE_URI": "mysql://root:123@127.0.0.1:3306/flaskdemo?charset=utf8mb4",
    # 如果使用pymysql,则需要在连接时指定pymysql
    # "SQLALCHEMY_DATABASE_URI": "mysql+pymysql://root:123@127.0.0.1:3306/flaskdemo?charset=utf8mb4"
    # 动态追踪修改设置,如未设置只会提示警告,设置False即可
    "SQLALCHEMY_TRACK_MODIFICATIONS": False,
    # ORM执行SQL查询时是哦否显示原始SQL语句,debug模式下可以开启
    "SQLALCHEMY_ECHO": True,
})

db.init_app(app)



class Student(db.Model):
    """学生管理"""
    __tablename__ = "db_student" # 表名
    # 属性名 = db.Column(字段类型, 字段列约束选项)
    # 如果SQL语句中的字段名在python中是关键字/保留字,则建议改写绑定字段名
    # 属性名 = db.Column("字段名", 字段类型, 字段列约束选项)
    id = db.Column("student_id", db.Integer, primary_key=True, comment="主键")
    name = db.Column(db.String(15), index=True, comment="姓名")
    age = db.Column(db.SmallInteger, comment="年龄")
    sex = db.Column(db.SmallInteger, comment="性别")
    email = db.Column(db.String(255), unique=True, comment="邮箱地址")
    money = db.Column(db.Numeric(10,2), default=0.0, comment="钱包")

    # repr()方法类似于django的__str__,用于打印模型对象时显示的字符串信息
    def __repr__(self):
        return f"{self.name}<{self.__class__.__name__}>"


# 所有的模型必须直接或间接继承于db.Model
class Course(db.Model):
    """课程数据模型"""
    __tablename__ = "db_course"
    """
    # 原生SQL语句
    create table db_course (
        id int primary key auto_increment comment "主键",
        name varchar(64) comment "课程",
        price NUMERIC(7,2) comment "价格",
        unique (name)
    );
    # 字段根据SQL语句来声明
    """
    id = db.Column(db.Integer, primary_key=True, comment="主键")
    name = db.Column(db.String(64), unique=True, comment="课程")
    price = db.Column(db.Numeric(7, 2), comment="价格")
    # repr()方法类似于django的__str__,用于打印模型对象时显示的字符串信息
    def __repr__(self):
        return f"{self.name}<{self.__class__.__name__}>"


class Teacher(db.Model):
    """老师数据模型"""
    __tablename__ = "db_teacher"
    """
    # 原生SQL语句
    create table db_teacher (
        id int primary key auto_increment comment "主键",
        name varchar(64) comment "姓名",
        option enum("讲师", "助教", "班主任") comment "职位",
        unique (`name`)
    );
    # 字段根据SQL语句来声明
    """
    id = db.Column(db.Integer, primary_key=True, comment="主键")
    name = db.Column(db.String(64), unique=True, comment="姓名")
    option = db.Column(db.Enum("讲师", "助教", "班主任"), default="讲师")

    def __repr__(self):
        return f"{self.name}<{self.__class__.__name__}>"


@app.route("/")
def index():
    return "ok"


@app.route("/create")
def create_table():
    db.create_all() # 为项目中被识别的所有模型创建数据表
    return "ok"


@app.route("/drop")
def drop_table():
    db.drop_all()   # 为项目中被识别的所有模型删除数据表
    return "ok"


"""一次添加一条数据"""
@app.route("/add")
def add_student():
    # 1. 先实例化要创建的模型对象
    student = Student(name="小明", age=17, email="xiaoming@qq.com", money=100)  # 实例化模型对象
    # 2. 把实例对象添加到连接会话
    db.session.add(student)
    # 3. 提交事务
    db.session.commit()
    return "ok"


"""一次添加多条数据"""
@app.route("/madd")
def multi_add():
    # 1. 先实例化要创建的模型对象
    student = Student(name="小红", age=17, email="xiaohong@qq.com", money=200)
    # 2. 把实例对象添加到连接会话
    db.session.add(student)
    # 1. 先实例化要创建的模型对象
    student = Student(name="小花", age=16, email="xiaohua@qq.com", money=200)
    # 2. 把实例对象添加到连接会话
    db.session.add(student)
    # 3. 只需要在结束的时候提交事务即可
    db.session.commit()
    return "ok"


@app.route("/madd2")
def multi_add2():
    # 1. 先创建的列表要添加的实例化模型对象列表
    student_list = [
        Student(name='wang', email='wang@163.com', age=20),
        Student(name='zhang', email='zhang@189.com', age=21),
        Student(name='chen', email='chen@126.com', age=19),
        Student(name='zhou', email='zhou@163.com', age=18),
        Student(name='tang', email='tang@163.com', age=16),
        Student(name='wu', email='wu@gmail.com', age=20),
        Student(name='qian', email='qian@gmail.com', age=21),
        Student(name='liu', email='liu@163.com', age=21),
        Student(name='li', email='li@163.com', age=18),
        Student(name='sun', email='sun@163.com', age=17),
    ]

    # 2. 一次性添加到连接会话中
    db.session.add_all(student_list)
    db.session.commit()

    return "ok"



@app.route("/del")
def delete_student():
    """删除一条数据"""
    # 先查询出来
    student = Student.query.first()
    # student = db.session.query(Student).first()
    # 再进行删除
    db.session.delete(student)
    db.session.commit()

    return "ok"


@app.route("/mdel")
def multi_delete_student():
    """按条件删除多条数据"""
    Student.query.filter(Student.id > 5).delete()
    # db.session.query(Student).filter(Student.id > 5).delete()
    db.session.commit()

    return "ok"


@app.route("/update")
def update():
    """更新一条"""
    # 先查询出来
    student = Student.query.filter(Student.id == 4).first()
    student.name = "小白"
    db.session.commit()
    return "ok"


@app.route("/update2")
def update2():
    """直接根据条件更新一条或多条数据"""
    Student.query.filter(Student.name == 'zhang', Student.money == -99.00).update({'money': 1998})
    db.session.commit()
    return "ok"


@app.route("/update3")
def update3():
    # 字段引用[利用当前一条数据的字典值进行辅助操作,实现类似django里面F函数的效果]
    # 每次自增100
    Student.query.filter(Student.name == "小花").update({"money": Student.money + 100})
    db.session.commit()
    return "ok"


@app.route("/update4")
def update4():
    # 字段引用[利用当前一条数据的字典值进行辅助操作,实现类似django里面F函数的效果]
    # 在原有money的基础上按age补贴1000*age
    Student.query.filter(Student.name == "zhang").update({"money": Student.money + 1000 * Student.age})
    db.session.commit()
    return "ok"

if __name__ == '__main__':
    app.run()
相关推荐
云和数据.ChenGuang40 分钟前
Django 应用安装脚本 – 如何将应用添加到 INSTALLED_APPS 设置中 原创
数据库·django·sqlite
woshilys1 小时前
sql server 查询对象的修改时间
运维·数据库·sqlserver
Hacker_LaoYi1 小时前
SQL注入的那些面试题总结
数据库·sql
建投数据2 小时前
建投数据与腾讯云数据库TDSQL完成产品兼容性互认证
数据库·腾讯云
Hacker_LaoYi3 小时前
【渗透技术总结】SQL手工注入总结
数据库·sql
岁月变迁呀3 小时前
Redis梳理
数据库·redis·缓存
独行soc3 小时前
#渗透测试#漏洞挖掘#红蓝攻防#护网#sql注入介绍06-基于子查询的SQL注入(Subquery-Based SQL Injection)
数据库·sql·安全·web安全·漏洞挖掘·hw
梧桐树04294 小时前
python常用内建模块:collections
python
Dream_Snowar4 小时前
速通Python 第三节
开发语言·python
你的微笑,乱了夏天4 小时前
linux centos 7 安装 mongodb7
数据库·mongodb