【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()
相关推荐
cuber膜拜1 小时前
jupyter使用 Token 认证登录
ide·python·jupyter
张登杰踩2 小时前
pytorch2.5实例教程
pytorch·python
codists2 小时前
《CPython Internals》阅读笔记:p353-p355
python
Change is good2 小时前
selenium定位元素的方法
python·xpath定位
Change is good2 小时前
selenium clear()方法清除文本框内容
python·selenium·测试工具
leegong231114 小时前
PostgreSQL 初中级认证可以一起学吗?
数据库
秋野酱5 小时前
如何在 Spring Boot 中实现自定义属性
java·数据库·spring boot
weisian1516 小时前
Mysql--实战篇--@Transactional失效场景及避免策略(@Transactional实现原理,失效场景,内部调用问题等)
数据库·mysql
AI航海家(Ethan)6 小时前
PostgreSQL数据库的运行机制和架构体系
数据库·postgresql·架构
大懒猫软件7 小时前
如何运用python爬虫获取大型资讯类网站文章,并同时导出pdf或word格式文本?
python·深度学习·自然语言处理·网络爬虫