Flask学习三:模型操作

ORM

flask 通过Model操作数据库,不管你的数据库是MySQL还是Sqlite,flask自动帮你生成相应数据库类型的sql语句,所以不需要关注sql语句和类型,对数据的操作flask帮我们自动完成,只需要会写Model就可以了

flask使用对象关系映射(简称为ORM)框架去操控数据库

ORM对象关系映射是一种程序技术,用于实现面向对象变成语言里不同类型系统的数据之间的转换,将对对象的操作转换为原生的SQL语句。简单易用,可以有效减少重复的SQL;性能损耗少;设计灵活,可以轻松实现复杂查询;可移植性好

数据库连接

Flask使用Python自带的ORM:SQLAlchemy。针对于Flask的支持,需要安装相应的插件

pip install flask-sqlalchemy

为什么使用sqlite而不是mysql

SQLite和MySQL是两种常见的数据库管理系统,它们之间有一些重要的区别。

  1. 数据库类型:SQLite是一种嵌入式数据库,它将整个数据库作为一个文件存储在磁盘上。而MySQL是一种客户端/服务器数据库,它以独立的服务器进程运行,并通过网络接口来处理客户端请求。

  2. 部署和配置:由于SQLite是一个嵌入式数据库,它非常容易部署和配置。你只需要在应用程序中包含SQLite的库文件即可。MySQL需要单独安装和配置数据库服务器,并通过网络连接来访问。

  3. 性能和扩展性:SQLite适用于小型应用程序和个人项目,因为它在处理大量数据和高并发访问时可能性能较低。相比之下,MySQL被设计用于处理大型数据集和高负载的应用程序,它具有更好的性能和扩展性。

  4. 功能和支持:MySQL是一个成熟的数据库管理系统,提供了广泛的功能和丰富的工具。它支持复杂的查询语言(如SQL),事务处理和高级安全特性。相比之下,SQLite是一个轻量级的数据库,功能相对较少。

  5. 并发处理:MySQL支持多个并发连接,可以同时处理多个客户端请求。SQLite在写操作时会锁定整个数据库文件,因此在高并发访问情况下可能会出现性能问题。

综上所述,SQLite适用于小型应用程序和个人项目,而MySQL适用于中大型应用程序和高负载环境。选择哪种数据库取决于你的具体需求和项目规模。

连接配置
直接看老师讲的视频好了,容易理解。千峰教育 Flask2入门 数据库连接

数据迁移

当数据库和表配置好后,需要进行数据迁移,这样会在项目中生成真正的数据库和表。
基本步骤如下:

  • 使用cmd打开项目目录(app.py所在的目录)
  • 依次输入下面的命令
    • flask db init 创建迁移文件夹migrates,只调用一次
    • flask db migrate 生成迁移文件
    • flask db upgrade 执行迁移文件中的升级
    • flask db downgrade 执行迁移文件中的降级

初始化
在这里插入图片描述
执行完会在项目根目录下生成对应的文件夹instancemigrations

在这里插入图片描述
生成迁移文件
在这里插入图片描述
会生成数据库db文件
在这里插入图片描述
并且会生成一个操作记录的py文件
在这里插入图片描述
生成表
在这里插入图片描述
实际上就是执行的上一次生成文件里的upgrade函数,执行完成后,我们可以使用类似dberver的工具来进行查看
在这里插入图片描述
修改表结构
在修改表结构时,比如新添加一列。必须要先修改对应的模型,然后在重新进行生成迁移文件、执行迁移文件中的升级。

数据库的常见操作

新增数据

@baseBlue.route("/add-user/")
def addUser():
    # 创建一个对象
    user = BaseModel()
    user.name = "张三"
    user.email = "2345@163.com"
    user.password = "123"
    user.create_time = datetime.now()
    # 将对象添加进session中
    db.session.add(user)
    # 将数据提交到数据库
    db.session.commit()
    data = {"code": 0, "data": '', "msg": "添加成功"}
    return jsonify(data)

在这里插入图片描述
在这里插入图片描述
如果添加多条数据时可以使用add_all,例如

 users =[]
 for i in range(10):
     user = BaseModel()
     user.name = "张三" + str(i)
     user.email = "2345@163.com"
     user.password = "123"
     user.create_time = datetime.now()
     users.append(user)
 
 db.session.add_all(users)
 db.session.commit()

在这里插入图片描述
同时可以添加异常处理

def addUser():
    # 创建一个对象
    user = BaseModel()
    user.name = "张三"
    user.email = "2345@163.com"
    user.password = "123"
    user.create_time = datetime.now()
    try:
        # 将对象添加进session中
        db.session.add(user)
        # 将数据提交到数据库
        db.session.commit()
        data = {"code": 0, "data": '', "msg": "添加成功"}
    except Exception as e:
        data = {"code": 1, "data": '', "msg": "添加失败"}
        db.session.rollback()  # 回滚
        db.session.flush()  # 清空session
        print("addUser error:"+str(e))
    return jsonify(data)

删除数据

def delUser(id):
    try:
        user = BaseModel.query.get(id)
        db.session.delete(user)
        db.session.commit()
        data = {"code": 0, "data": '', "msg": "删除成功"}
    except Exception as e:
        data = {"code": 1, "data": '', "msg": "删除失败"}
        db.session.rollback()  # 回滚
        db.session.flush()  # 清空session
        print("delUser error:"+str(e))
    return jsonify(data)

修改数据

def updateUser(id):
    try:
        user = BaseModel.query.get(id)
        user.name="李四"
        db.session.commit()
        data = {"code": 0, "data": '', "msg": "修改成功"}
    except Exception as e:
        data = {"code": 1, "data": '', "msg": "修改失败"}
        db.session.rollback()  # 回滚
        db.session.flush()  # 清空session
        print("updateUser error:"+str(e))
    return jsonify(data)

查询数据

过滤器

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

常用查询

  • all() 以列表形式返回查询的所有结果,返回列表
  • first() 返回查询的第一个结果,如果没有结果返回None
  • get() 返回指定主键对应的行,如果没有对应的行,则返回None
  • count() 返回查询结果的数量
  • paginate() 返回一个Paginate对象,它包含指定范围内的结果

逻辑运算

  • and_ 逻辑与,filter()默认为逻辑与,多个条件时用逗号分隔
  • or_ 逻辑或
  • not_ 逻辑非

查询属性

  • contains()contains('3') 模糊查找,类似于sql中的like
  • in_()in_([1,2,3]) 其中之一
  • startswith()endswidth() 以什么开始和以什么结束

根据用户名模糊查询

def queryUser(name):
    try:
        users = BaseModel.query.filter(BaseModel.name.like("%"+name+"%")).all()
        res = [user.to_dict() for user in users]
        data = {"code": 0, "data": res, "msg": "查询成功"}
    except Exception as e:
        data = {"code": 1, "data": '', "msg": "查询失败"}
        print("queryUser error:"+str(e))
    return jsonify(data)

注意:查询返回的结果是模型对象,因此需要将模型对象转换为字典

class BaseModel(db.Model):
    # 表的名字
    __tablename__ = 'base_user'

    # 表的结构
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)  # 主键
    name = db.Column(db.String(20), unique=False, nullable=False)  # 用户名
    email = db.Column(db.String(20), unique=True, nullable=False)  # 邮箱
    password = db.Column(db.String(20), nullable=False)  # 密码
    rank = db.Column(db.String(20), nullable=False,
                     default='user')  # 用户等级,admin为管理员,user为普通用户
    create_time = db.Column(db.DateTime, nullable=False)  # 创建时间

    # 将模型对象转换为字典
    def to_dict(self):
        return {c.name: getattr(self, c.name) for c in self.__table__.columns}

在这里插入图片描述
query.filter就相当于sql中的where查询,如果存在多个查询条件时可以如下操作

from sqlalchemy import and_, or_

# 示例查询条件
condition1 = BaseModel.column1 == value1
condition2 = BaseModel.column2 > value2
condition3 = BaseModel.column3.like("%" + value3 + "%")

# 使用 and_ 方法将多个条件组合为与关系
query = BaseModel.query.filter(and_(condition1, condition2, condition3))

# 或者使用 or_ 方法将多个条件组合为或关系
query = BaseModel.query.filter(or_(condition1, condition2, condition3))

# 执行查询
results = query.all()

或者

from sqlalchemy import or_

conditions = [
    User.username == 'user1',
    User.id > 5,
    # ...
]

query = User.query
query = query.filter(or_(*conditions))

users = query.all()

分页查询

SQLAlchemy内部提供了paginate方法来实现分页的功能。

# 分页查询
@baseBlue.route("/get-all-user", methods=['POST'])
def getAllUser():
    # 获取前端传递过来的参数
    page = int(request.form.get("page"))
    page_size = int(request.form.get("pageSize"))
    p = BaseModel.query.paginate(
        page=page, per_page=page_size, error_out=False)
    # 将模型对象转换为字典
    data = [item.to_dict() for item in p.items]
    res = {
        "code": 0,
        "data": {
            "total": p.total,
            "page": p.page,
            "pageSize": p.per_page,
            "list": data
        },
        "msg": "查询成功"
    }
    return jsonify(res)

paginate对象的属性:

  • items:返回当前页的内容列表
  • has_next:是否还有下一页
  • has_prev:是否还有上一页
  • next(error_out=False):返回下一页的Pagination对象
  • prev(error_out=False):返回上一页的Pagination对象
  • page:当前的页码(从1)开始
  • pages:总页数
  • pre_page:每页显示的数量
  • prev_num:上一页页码数
  • next_num:下一页页码数
  • total:查询返回的记录总数

在这里插入图片描述
在这里插入图片描述

多表操作

一对多

学生、班级表

class Grade(db.Model):
    # 表名
    __tablename__ = 'grade'
    # 表结构
    id = db.Column(db.Integer, primary_key=True, autoincrement=True, doc="主键")
    name = db.Column(db.String(10), unique=True, nullable=False, doc="班级名称")
    # 建立联系
    # 参数1:表名,类的名称,这里要用字符串
    # 参数2:反向引用的名称,grade对象,让student去反过来得到grade对象的名称
    #                      这样Student表可以直接使用Grade中的字段,而不是使用班级id再去表里查询
    # 参数3:懒加载
    # 这里的students不是表字段,是一个类属性
    students = db.relationship('Student', backref="grade", lazy=dynamic)


# 学生表
class Student(db.Model):
    __tablename__ = 'student'
    # 字段
    id = db.Column(db.Integer, primary_key=True, autoincrement=True, doc="主键")
    name = db.Column(db.String(10), unique=True, nullable=False, doc="学生姓名")
    grade_id = db.Column(db.Integer, db.ForeignKey(
        'grade.id'), nullable=False, doc="班级id")

lazy属性

lazy属性:懒加载,可以延迟在使用关联属性的时候才建立关联

  • lazy='dynamic':会返回一个query对象(查询集),可以继续使用其他的查询方法,比如all()
  • lazy='select':首次访问到属性的时候,就会全部加载该属性的数据
  • lazy='joined':在对关联的两个表进行join操作是,从而获取到所有相关的对象
  • lazy=True:返回一个可用的列表对象,同select

增加数据

def addGradeStudent():
    student = Student()
    student.name = '张三'
    student.grade_id = 1
    try:
        db.session.add(student)
        db.session.commit()
        data = {"code": 0, "data": '', "msg": "添加成功"}
    except Exception as e:
        data = {"code": 1, "data": '', "msg": "添加失败"}
        db.session.rollback()  # 回滚
        db.session.flush()  # 清空session

修改、删除操作基本上也与单表操作一致。需要注意的是,比如在删除班级时,这个班级里有学生。删除后学习表里的学生不会被删除,但是学习对应的班级id会变为null

查询

# 查询学生所在班级的班级名称
def getGradeName():
    try:
        student = Student.query.get(1)
        # 这里使用了反向引用,这样就不许要再查询班级表
        gradeName = student.grade.name
        res = {"code": 0, "data": gradeName, "msg": "查询成功"}
        data = {"code": 0, "data": res, "msg": "查询成功"}
    except Exception as e:
        data = {"code": 1, "data": '', "msg": "查询失败"}
        print("getGradeName error:"+str(e))
# 查询班级下的所有学生
    grade = Grade.query.get(1)
    students = grade.students

多对多

多对多需要创建一个中间表,比如用户收藏电影,需要有用户表、收藏表、电影表,收藏表就是这个中间表。

用户表、电影表、收藏表

# 用户表
class User(db.Model):
    __tablename__ = 'user'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True, doc="主键")
    username = db.Column(db.String(10), unique=True, nullable=False, doc="用户名")
    # 类属性,不是表字段。
    collections = db.relationship("Collection", back_populates="user")

# 电影表
class movie(db.Model):
    __tablename__ = 'movie'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True, doc="主键")
    name = db.Column(db.String(10), unique=True, nullable=False, doc="电影名称")
    score = db.Column(db.Float, nullable=False, doc="评分")
    collections = db.relationship("Collection", back_populates="movie")

# 收藏表
class Collection(db.Model):
    id = db.Column(db.Integer, primary_key=True,doc="主键")
    user_id = db.Column(db.Integer, db.ForeignKey("user.id"),doc="用户id")
    movie_id = db.Column(db.Integer, db.ForeignKey("movie.id"),doc="电影id")

    user = db.relationship("User", back_populates="collections")
    movie = db.relationship("Movie", back_populates="collections")

back_populatesSQLAlchemy 关系的一个参数,用于指定两个模型之间的双向关联关系。当在一个模型类中定义了一个关系并指定了另一个模型类时,back_populates 参数允许你在另一个模型类中定义一个相反方向的关系。

backref 不同,back_populates 必须在双方都明确声明,而 backref 只需要在一方声明即可自动创建另一方的反向引用。back_populates 更适合于复杂的关系映射和控制反向引用的名称。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/251177.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

[渗透测试学习] Keeper - HackTheBox

信息搜集 nmap扫描一下端口 nmap -sV -sC -p- -v --min-rate 1000 10.10.11.227发现有两个端口,22端口为ssh服务,80端口是http服务 尝试访问一下80端口 提示我们要访问tickets.keeper.htb/rt/,访问发现不行 那么把该域名添加到hosts里面 …

数据库——关系数据的规范化:范式判断【知识点罗列+例题讲解】

知识点罗列: 各种范式之间的关系 1.第一范式1NF: 如果关系模式R中所有的属性都具有原子性,均是不可再分的(一个属性不能再被分解成更小的数据单元),则称R属于第一范式,简称1NF,记作R…

springMVC-模型数据的处理

一、数据放入到request域当中 1、把获取的数据放入request域中&#xff0c; 方便在跳转页面去显示 <a>添加主人信息</a> <form action"vote/vote04" method"post" >主人id&#xff1a;<input type"text" name"id&q…

Redis List类型

列表类型是用来存储多个有序的字符串&#xff0c;如图所示&#xff0c;a、b、c、d、e 五个元素从左到右组成了一个有序的列表&#xff0c;列表中的每个字符串称为元素 (element)&#xff0c;一个列表最多可以存储2的32次方 -1个元素。在 Redis 中&#xff0c;可以对列表两端插入…

Kafka-Kafka核心参数详解

Kafka的HighLevel API使用是非常简单的&#xff0c;所以梳理模型时也要尽量简单化&#xff0c;主线清晰&#xff0c;细节慢慢扩展。 Kafka提供了两套客户端API&#xff0c;HighLevel API和LowLevel API。 HighLevel API封装了kafka的运行细节&#xff0c;使用起来比较简单&…

RabbitMQ消息顺序性保障

RabbitMQ 没有属性设置消息的顺序性&#xff0c;只能设置消息的优先级&#xff0c;因此消息顺序性保障只能在 consumer 上实现 场景分析&#xff1a; 生产者向 RabbitMQ 里发送了三条数据&#xff0c; 顺序依次是 data1-> data2 -> data3&#xff0c;压入的是一个内存…

DeepCorr:利用深度学习进行Tor的流关联攻击

文章信息 论文题目&#xff1a;DeepCorr: Strong Flow Correlation Attacks on Tor Using Deep Learning 期刊&#xff08;会议&#xff09;&#xff1a;Proceedings of the 2018 ACM SIGSAC Conference on Computer and Communications Security 时间&#xff1a;2018 级别&a…

android下的app性能测试应主要针对那些方面,如何开展?

如何开展安卓手机下的App性能测试&#xff0c;对于优秀的测试人员而言&#xff0c;除了要懂得性能测试的步骤流程外&#xff0c;还应该懂的性能测试的一些其他知识&#xff0c;比如性能测试指标、各指标的意义&#xff0c;常用的性能测试工具、如何查看结果分析等等知识。所以本…

ES6 面试题 | 13.精选 ES6 面试题

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

(五)STM32 按键输入实验及 GPIO做普通 IO 的注意事项

目录 1. 按键硬件连接 2. 按键软件设计 3. 按键消抖 4. 使用 IO 口时的 注意事项&#xff08;踩坑&#xff09; 上一节我们介绍了 STM32F1 的 IO 口作为输出的使用&#xff0c;这一章&#xff0c;我们将介绍如何使用 STM32F1 的 IO 口作为输入用。在本章中&#xff0c;我们…

网络(十)ACL和NAT

前言 网络管理在生产环境和生活中&#xff0c;如何实现拒绝不希望的访问连接&#xff0c;同时又要允许正常的访问连接&#xff1f;当下公网地址消耗殆尽&#xff0c;且公网IP地址费用昂贵&#xff0c;企业访问Internet全部使用公网IP地址不够现实&#xff0c;如何让私网地址也…

Java 基础学习(十一)File类与I/O操作

1 File类 1.1 File类概述 1.1.1 什么是File类 File是java.io包下作为文件和目录的类。File类定义了一些与平台无关的方法来操作文件&#xff0c;通过调用File类中的方法可以得到文件和目录的描述信息&#xff0c;包括名称、所在路径、读写性和长度等&#xff0c;还可以对文件…

力扣LCR 130. 衣橱整理(DFS 解法)

Problem: LCR 130. 衣橱整理 文章目录 题目描述思路解题方法复杂度Code 题目描述 思路 首先该问题可以归纳为一类遍历二维矩阵的题目&#xff0c;此类中的一部分题目可以利用DFS来解决&#xff0c;具体到本题目&#xff1a; 我们可以利用一个布尔类型的二维数组记录我们已经访…

LeetCode(65)LRU 缓存【链表】【中等】

目录 1.题目2.答案3.提交结果截图 链接&#xff1a; LRU 缓存 1.题目 请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。 实现 LRUCache 类&#xff1a; LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存int get(int key) 如果关键字 k…

Vue3使用了Vite和UnoCSS导致前端项目启动报错:Error:EMFILE:too many open files

一个 Vue3 的项目&#xff0c;用的是 Vite 打包&#xff0c;通过 npm run dev 运行时&#xff0c;遇到了以下错误&#xff08;尤其是引入了 Element-Plus 后&#xff09;&#xff1a; Error: EMFILE: too many open files&#xff0c;后面是具体的文件路径。。甚至到了 node_mo…

基于 Gin 的 HTTP 代理上网行为记录 demo

前言: 前端时间写了好几篇使用 Gin 框架来做 HTTP 代理 demo 的文章&#xff0c;然后就想着做一个记录上网行为的小工具&#xff0c;就是简单记录看看平时访问了什么网站&#xff08;基于隧道代理的&#xff0c;不是中间人代理&#xff0c;所以只能记录去了哪里&#xff0c;不能…

vue3:直接修改reative的值,页面却不响应,这是什么情况?

目录 前言&#xff1a; 错误示范&#xff1a; reactive() 的局限性 解决办法&#xff1a; 1.使用ref 2.reative多套一层 3.使用Object.assign 前言&#xff1a; 今天看到有人在提问&#xff0c;问题是这样的&#xff0c;我修改了reative的值&#xff0c;数据居然失去了响…

详细了解stm32---按键

提示&#xff1a;永远支持知识文档免费开源&#xff0c;喜欢的朋友们&#xff0c;点个关注吧&#xff01;蟹蟹&#xff01; 目录 一、了解按键 二、stm32f103按键分析 三、按键应用 一、了解按键 同学们&#xff0c;又见面了o(*&#xffe3;▽&#xffe3;*)ブ&#xff0c;最…

【Java代码审计】XSS篇

【Java代码审计】XSS篇 1.Java中XSS常见触发位置2.反射型XSS3.存储型XSS4.XSS漏洞修复 1.Java中XSS常见触发位置 XSS漏洞产生后必然会有相关的输入/输出&#xff0c;因此我们只需快速找到这些输入/输出点&#xff0c;即可快速地进行跟踪发现漏洞。输入在Java中通常使用“reque…

ES6 面试题 | 02.精选 ES6 面试题

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…