flask的ORM操作

2023-10-27

flask的ORM操作

ORM

ORM(Object Relational Mapping)中文翻译过来叫作对象关系映射,它把一个类映射成数据库里的一张表而属性映射成数据库表的列,每一个实例对象对应数据库表里的一行数据。通过它我们可以直接使用面向对象的方式来编写程序而不再直接书写原生的SQL语句,而且大部分ORM框架支持多种数据库只需很少的配置即可完成数据库产品的更换,在Python中常见的ORM框架有SQLAlchemy、Django Model、PeeWee等。

Flask-SQLAlchemy扩展

SQLAlchemy是目前很流行的ORM框架,Flask-SQLAlchemy扩展可以帮助我们在Flask中方便的使用SQLAlchemy

安装Flask-SQLAlchemy扩展,假如你没有安装SQLAlchemy会一并安装上

pip install flask-sqlalchemy

数据模型

模型是对数据抽象并提供通用访问接口的一种方式,一个模型类对应于数据库中的一张表。

定义模型类

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///test.db"

db = SQLAlchemy(app)


# 分类
class Category(db.Model):
    __tablename__ = "t_category"
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(50))

db.create_all()

代码说明: 所有模型必须继承至 db.Model,使用 __tablename__ 属性指定数据库中表名,如果不指定表名默认为类名。模型类的属性对应于数据表中的列,通过 db.Column 类的实例来描述,最后 db.create_all() 会帮助我们创建表结构

连接数据库的信息通过app.config[“SQLALCHEMY_DATABASE_URI”]来指定,常见数据库的连接信息如下

数据库 连接地址
SQLite sqlite:///文件名
MySQL mysql+pymysql://用户名:密码@主机:3306/数据库名
Oracle oracle+cx_oracle://用户名:密码@主机:1521/SID名
SQL Server mssql+pymssql://用户名:密码@主机:端口/数据库名

注: 上面有些URL中的协议部分有加号需要安装指定模块,如MySQL需要安装PyMySQL模块如果不书写加号部分会默认使用MySQLDB进行连接

描述列时常用的数据类型

类型名 Python 类型 说明
Integer int 普通整数,一般是32位
SmallInteger int 取值范围小的整数,一般是 16 位
BigInteger int 或 long 不限制精度的整数
Float float 浮点数
Double float 双精度浮点类型
Numeric decimal.Decimal 定点数(默认decimal.Decimal,asdecimal=False时转float)
String str 变长字符串
Text str 编程字符串,对较长或不限长度的字符串做了优化
Boolean bool 布尔值
Date datetime.date 日期
Time datetime.time 时间
DateTime datetime.datetime 日期和时间
Interval datetime.timedelta 时间间隔
Enum Enum 枚举类型
ARRAY array 数组
PickleType 任何 python 对象 自动使用 Pickle 序列化
Unicode unicode 变长 Unicode 字符串
UnicodeText unicode 变长 Unicode 字符串,对较长或不限长度的字符串做了优化
LargeBinary binary 大型二进制字节数据

描述列时还可以添加选项,常见如下

选项名 说明
primary_key 设置主键
default 设置默认值
unique 设置是否唯一
nullable 设置是否允许为空
index 设置是否添加索引
autoincrement 自增
comment 注释

注: SQLAlchemy要求每个模型必须定义主键默认对主键进行自增,默认映射到数据库中的列名为自定义类的属性名称,如果想修改该值需要在db.Column()第一个参数指定
添加索引

# 方式一
class Category(db.Model):
    __tablename__ = "t_category"
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(50),index =True)

# 方式二
class Category(db.Model):
    __tablename__ = "t_category"
    __table_args__ = (
		db.Index('idx_name_user', 'user_id', 'name', unique=True),
	)
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(50))
    user_id = db.Column(db.String(50))

模型之间的关联

模型之间可以互相关联使得相关联的数据能够很容易的从数据库中取出,模型关联映射到数据库中为主外键关系。

一对多/多对一
这是最常见的一种关系,比如说一个分类下面有多篇文章

# 分类
class Category(db.Model):
    __tablename__ = "t_category"
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(50))
    posts = db.relationship("Post", backref="category")

# 文章
class Post(db.Model):
    __tablename__ = "t_post"
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(50))
    content = db.Column(db.Text)
    category_id = db.Column(db.Integer, db.ForeignKey("t_category.id"))

代码说明: 在多的一端通过 db.ForeignKey 定义外键指向一的一端的主键列,该参数值格式为:表名.列名;通过 db.relationship 指定关系,第一个参数填写有关系的模型类,当该模型类还未定义可以先填写字符串底层会通过反射来获取,通过backref参数向有关系的一端添加一个可以引用自己的属性,该语句也可以定义在关系中的任意一方但必须指定的关系要明确。

一对一
常见的场景有一个人只有一个身份证,只需要把一对多关系中多的一端的外键指定为唯一值即可

# 人
class People(db.Model):
    __tablename__ = "t_people"
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(15))
    codes = db.relationship("Code", backref="people")

# 身份证
class Code(db.Model):
    __tablename__ = "t_code"
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(18))
    p_id = db.Column(db.Integer, db.ForeignKey("t_people.id"), unique=True)

多对多
常见的场景有一个用户对应多个角色,而一个角色同时也能对应多个用户,在关系型数据库中一般通过中间表的形式体现

方式一: 使用中间模型帮我们建立中间表

# 用户
class User(db.Model):
    __tablename__ = "t_user"
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(15))
    user_roles = db.relationship("UserRole", backref="user")

# 角色
class Role(db.Model):
    __tablename__ = "t_role"
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(15))
    role_users = db.relationship("UserRole", backref="role")

# 关系
class UserRole(db.Model):
    __tablename__ = "t_user_role"
    user_id = db.Column(db.Integer, db.ForeignKey("t_user.id"), primary_key=True)
    role_id = db.Column(db.Integer, db.ForeignKey("t_role.id"), primary_key=True)

方式二: 手动创建中间表

t_user_role = db.Table("t_user_role",
    db.Column("user_id", db.Integer, db.ForeignKey("t_user.id"), primary_key=True),
    db.Column("role_id", db.Integer, db.ForeignKey("t_role.id"), primary_key=True)
)

# 用户
class User(db.Model):
    __tablename__ = "t_user"
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(15))
    roles = db.relationship("Role", backref="users", secondary=t_user_role)

# 角色
class Role(db.Model):
    __tablename__ = "t_role"
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(15))

以上两种方式都能实现多对多关系,我个人更喜欢方式二因为方式一多了一个模型类操作起来更繁琐

表管理

在上面的例子中我们只使用了db.create_all(),他会扫描模型创建所有的表,如果想删除所有表使用如下语句

db.drop_all()

当然在实际项目中我们不会在代码中管理表一般会借助工具来生成,如使用Flask-Script扩展写一个命令函数,在Shell中执行上面的代码,更高级的使用Flask-Migrate扩展它不仅能创建表还能进行数据迁移。

操作数据

有了模型类也生成了表,接下来通过模型类对数据进行增删改查

新增

只需构建模型类的实例,添加进数据库会话即可

c = Category(name="Java")
db.session.add(c)

对于一对多/多对一关联关系

p = Post()
p.title = "Java入门示例"
p.content = "Hello World ...."
p.category = c
db.session.add(p)

对于多对多关系,下例采用方式二定义的多对多关系,至于方式一定义的关联关系操作与一对多类似

u = User(name="Harmel")
r1 = Role(name="ROOT")
r2 = Role(name="NONE")
db.session.add(r1)
db.session.add(r2)
u.roles = [r1, r2]
db.session.add(u)

# 也可以采用如下方式
u.roles.append(r1)
u.roles.append(r2)
修改

把查出来的对象更改属性后再次保存即可修改

# 根据ID查询
c = Category.query.get(1)
c.name = "Python"
db.session.add(c)# 也可以直接db.session.commit()
删除

显示删除

c = Category.query.get(1)
db.session.delete(c)

隐式删除就是在一对一或一对多中配置级联删除后,如果删除一的一端另一端的数据也会同步删除

批量删除/全量删除

#方法一(批量删除)
 bills = Bill.query.filter(Bill.user_id ==1).all()
 for bill in bills:
 	db.session.delete(bill)
 db.session.commit()
 #方法二(全量删除)
 Bill.query.filter(Bill.user_id ==1).delete(synchronize_session=False)
 db.session.commit()

synchronize_session用于query在进行delete or update操作时,对session的同步策略。

参数:
False ------- 不对session进行同步,直接进行delete or update操作。

’fetch’ ------- 在delete or update操作之前,先发一条sql到数据库获取符合条件的记录。在delete or update操作之后,将session的identity_map与前一步获取到的记录进行match,符合条件的就从session中删掉或更新。
’evaluate’ ------- 在delete or update操作之前,用query中的条件直接对session的identity_map中的objects进行eval操作,将符合条件的记录下来。在deleteor update操作之后,将符合条件的记录删除或更新。

事务

对于每次的增、删、改操作都必须要提交事务

db.session.commit()

我们可以配置让其自动提交

app.config["SQLALCHEMY_COMMIT_ON_TEARDOWN"] = False

回滚事务

db.session.rollback()
查询

django是有orm操作的 可想而知 那么flask也是有orm操作的,其实flask的orm操作的使用和djnago的是差不多的

django的orm操作进行条件筛选的时候后面跟着的是objects

django
  表名.objects.语句
flask的是query
  表名.objects.语句
 
eg:
  django:
    User.objects.filter(条件).first
  flask:
    User.query.filter_by(条件).first

常用查询语句:

all()     查询所有
 
filter_by / filter  单个查询
    filter_by  不需要指定是哪个类的哪个属性,只需要制定属性及其目标值就可以了, 并且只能写具体的值不能写模糊值
    filter    filter中指定查询条件的时候需要指定类名的前缀。可以指定模糊值
 
order_by    排序

查询集

  1. 原始查询集
    类名.query得到的结果就为原始查询集

  2. 数据查询集
    加上各种的过滤器的方法 最终返回的结果 为数据查询集 都使用数据查询集

过滤器
(1) all 查询所有 以列表形式返回 不支持连贯操作
类名.query.all()

User.query.all()   # 查询User表中的所有数据

(2) filter() 过滤
类名.query.filter([类名.属性名 条件操作符 值])

 User.query.filter() #返回所有
 
 User.query.filter(User.age>) #查询年龄大于20的数据
 
 User.query.filter(User.age>,User.age<) #查询年龄大于20的数据 and 小于40

(3) filter_by 只支持参数为关键字参数
类名.query.filter_by(属性名=值…)

 data = User.query.filter_by(id=12)
 
 data = User.query.filter_by(id>12) #错误写法 不可以使用模糊查到
 
 data = User.query.filter_by(id=12,age=20)

(4) offset 偏移量
offset(num)

User.query.filter().offset()

(5) limit 取值
limit(num)

User.query.filter(User.age>18).limit()   查到的结果只取两个

(6) offset和limit组合使用

 User.query.offset().limit()  也是只取两个

(7) order_by() 排序
默认是升序

data = User.query.order_by(User.age).all() #升序
data = User.query.order_by(-User.age).all() #降序
data = User.query.order_by(User.age.desc()).all() #降序

(8) first 取出第一条数据 返回对象

User.query.first() == User.query.get()

(9) get 获取id对应的数据
查询成功返回对象 查询失败 返回None

User.query.get(12)

(10) contains 包含关系

User.query.filter(User.username.contains('7'))    #username中包含数字7的数据

(11) like 模糊查询

 User.query.filter(User.username.like('李%')) #以李作为开头的

(12) startswith endswith 以…开头 以…结尾

 User.query.filter(User.username.startswith('李'))   # 以姓李的开头
 User.query.filter(User.username.endswith(''))    # 以6为结尾的

(13) 比较运算符

 1. __gt__
 2.  __ge__
 3.  __lt__
 4. __le__
 5. > <
 6. >= 
 7. <=
 8. == 
 9. != 

注意:
当查询数据中包含NULL结果时,通过!= 的查询会过虐掉为NULL的对象
所以需要将为NULL的对象包含进来

User.query.filter(or_(Order.school.is_(None),Order.school!= 'THU')).all()

(14) in 和 not in

User.query.filter(User.age.in_([12,23,36]))
User.query.filter(User.age.notin_([12,23,36]))

(15) is null

#空
User.query.filter(User.username.is_(None))
User.query.filter(User.username ==None)
#非空
User.query.filter(User.username.isnot(None))
User.query.filter(User.username !=None)

(16) and_

多个条件 用逗号隔开,为and操作

from sqlalchemy import and_

 User.query.filter(and_(User.age==18,User.id==12))

(17) or_
from sqlalchemy import or_

@main.route('/and/')
def myAnd():
    data = User.query.filter(or_(User.age==18,User.id==12))
    data = User.query.filter(and_(User.username.like('%6%')),or_(User.age>=18,User.id==12))
    return render_template('show.html',data=data)

(18) not_
from sqlalchemy import not_

@main.route('/and/')
def myAnd():
    # data = User.query.filter(not_(User.age>18,User.id==12))
    #错误写法只能给一个条件取反
    data = User.query.filter(not_(User.age>18))
    return render_template('show.html',data=data)

(19) count 统计

data = User.query.filter(not_(User.age>18)).count()
Flask-SQLAlchemy提供了分页方法
# 第一个参数指定第几页
# 第二个参数指定每页多少条数据
paginate = Category.query.paginate(1, 5)
categorys = paginate.items
page_count = paginate.pages # 总页数
has_prev = paginate.has_prev # 是否有上一页
has_next = paginate.has_next # 是否有下一页
prev_paginate = paginate.prev() # 上一页
next_paginate = paginate.next() # 下一页

四、文件的迁移

模块:

pip install flask-migrate
 
pip install flask-script

使用
(1) 实例化

from flask_migrate import Migrate,MigrateCommand
from flask_sqlalchemy import SQLalchemy
app = Flask(__name__)
db = SQLalchemy(app)
migrate = Migrate(app,db=db)
manager = Manager(app)
manager.add_command('db',MigrateCommand)

(2) 初始化 迁移文件目录

python manage.py db init

(3) 生成迁移文件

python manage.py db migrate

(4) 执行迁移文件

python manage.py db upgrade

注意

如果当前存在 模型 但是执行创建迁移文件的时候 提示没有任何改变的时候 需要查看当前的模型类是否有使用(导入)

flask-sqlalchemy的使用

flask-sqlalchemy的使用要么放置在一个视图内,要么提供一个应用(flask)上下文
官方文档给出了如下解释:
在这里插入图片描述
简单来说就是如果要在视图函数以外使用sqlalchemy就必须通过app.app_context().push()来推入一个上下文,第二个是通过with上下文来确定作用在APP上下文区域内的代码.不然就会报No application found. Either work inside a view function or push an application context.错误
个人觉得还是通过装饰器的方式来的方便和美观,当然第二种方式也相当优美.

下面是我的解决方法:

def sqlalchemy_context(app):
    def add_context(func):
        @wraps(func)
        def do_job(*args, **kwargs):
            app.app_context().push()
            result = func(*args,**kwargs)
            return result
        return do_job
    return add_context

然后我使用数据库的地方:

@sqlalchemy_context(APP)
def init_primary_key():
    Model.query.filter_by()
    ...

我APP传入方式是因为避免循环导包, 思路是这样,实现方式自己把握好了.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

flask的ORM操作 的相关文章

随机推荐

  • Web渗透测试实战——(1)Web渗透测试简介

    一 什么是渗透测试 渗透测试 penetration testing 是对计算机系统的一种授权攻击 旨在评估系统 网络的安全性 执行测试以识别漏洞及其带来的风险 一般而言 渗透测试过程分为五个阶段 包括识别目标系统 检测存在的漏洞以及每个漏
  • Java基础系列30-单列 Collection集合

    文章目录 一 集合的概述 1 1 为什么会出现集合类 1 2 集合类体系结构图 二 Collection集合 2 1 Collection集合入门 2 2 Collection集合的成员方法 2 3 Collection集合的遍历 2 4
  • vue使用vue-amap 高德地图进行选点和搜索

    vue使用vue amap进行地图点的搜索和点击选点 npm install vue amap save 下载npm 包 在main js主文件中进行引用 import VueAMap from vue amap Vue use VueAM
  • C++连接CTP接口实现简单量化交易(行情、交易、k线、策略)

    对于量化交易来说 量化策略和技术系统缺一不可 为了知其所以然 本文实现了一个C 连接CTP接口进行仿真交易的demo 从接收行情 下订单 数据处理到添加策略 挂载运行交易等多个环节来看一下量化交易的最简单流程 管中窥豹 一探究竟 准备工作
  • NetworkManager 使用

    Network Manager Network Manager aims for Network Connectivity which Just Works The computer should use the wired network
  • clickhouse源码:函数分析和自定义函数UDF

    clickhouse函数介绍 clickhouse官方提供了许多的函数 包括常规的数学函数 聚合函数 时间函数 逻辑函数 比较函数等等 关于官方的函数可以在官方文档中查看 官方文档 当然随着clickhouse的流行 国内也有不少的博主已经
  • 《星岛日报》专访:欧科云链AML,助力数字资产合规及风险防控

    6月1日 香港 适用于虚拟资产交易平台营运者的指引 及 打击洗钱指引 正式施行 香港虚拟资产发牌制度正式生效 作为深耕香港市场多年的Web3科技企业 欧科云链OKLink也正式推出的Onchain AML反洗钱合规解决方案 利用多年积累的海
  • python如何建立一个空集合_python的集合set

    一 集合set 概念 1 集合set是一组无序不可重复的key集合 2 set跟dict的key类似 区别在于set没有value 3 set使用场景 1 判断某个元素是否在集合中 2 消除输入数据的重复元素 二 set 的创建方式 1 创
  • timm 视觉库中的 create_model 函数详解

    timm 视觉库中的 create model 函数详解 最近一年 Vision Transformer 及其相关改进的工作层出不穷 在他们开源的代码中 大部分都用到了这样一个库 timm 各位炼丹师应该已经想必已经对其无比熟悉了 本文将介
  • 经典不衰数据可视化项目第一节(共享单车项目)

    1 1 首先导入包 import pandas as pd import numpy as np import matplotlib pyplot as plt import seaborn as sns import datetime i
  • Dectorator:装饰模式

    要为一个类扩展功能 那么可以使用派生 然后由派生类实现扩展的功能 但 若扩展的是个临时功能 或者可能扩展多个不确定功能 就需要使用装饰模式 装饰模式允许为一个类临时添加任意其他功能 并可以任意组合 装饰模式可以在不生成子类的情况下为对象添加
  • 情境领导者-第四章、选择合适的领导风格

    文章目录 情境领导者 第四章 选择合适的领导风格 故事 背景 情境领导模式 每种领到风格中 领导者可以有若干的选择 使用情境领导模式 结束语 情境领导者 第四章 选择合适的领导风格 故事 罗杰斯 让我给你讲一个例子 当我刚开始这里工作的时候
  • strncpy_s

    include stdafx h include
  • 记录.原码、反码、补码是如何简化处理四则运算以及计算机的数值存储

    一 原码反码补码 1 原码 最高位是符号位 正数符号位为0 负数符号位为1 其余是数值 符号位加数值组成原码 2 反码 正数补码是原码 负数补码是除了符号位外其他位取反 3 补码 正数补码是原码 负数补码是其反码加1 二 问题 1 在学习v
  • SQLServer 2008 下载地址(微软官方网站)

    SQLServer 2008 下载地址 微软官方网站 中文版 3 28GB http sqlserver dlservice microsoft com dl download B 8 0 B808AF59 7619 4A71 A447 F
  • 2021年数模国赛B题--乙醇偶合制备 C4 烯烃

    一 题目 C4 烯烃广泛应用于化工产品及医药的生产 乙醇是生产制备 C4 烯烃的原料 在制备过程中 催化剂组合 即 Co 负载量 Co SiO2 和 HAP 装料比 乙醇浓度的组合 与温度对 C4 烯烃的选择性和 C4 烯烃收率将产生影响
  • 前端面试——实现数组的复制

  • 来了!Goby一年一度的红队专版正式发布!!

    某大型活动临近 盆圈开始热闹起来 各种抢人大赛以及群集结号声 Goby今年有什么动作呢 继续支持攻击队 红队 也称蓝军 为何有且仅有红队专版 如果对方是一位绝世武功高手且拥有绝世宝剑 你肯定没有信心与他一战 但如果给你配一把枪呢 哪怕是一把
  • 关于微信小程序复制到剪贴板显示默认提示词的问题

    早上一上班 看到一个需求 小程序实现点击复制到剪贴板 在开发文档找了 剪贴板 关键词 就直接找到了相关的接口 如图 心里很是欣慰 结果如下 提示词显示固定 不能修改任何东西 在社区里找了一下 得到的结果 暂未解决 针对这一问题 网上解决办法
  • flask的ORM操作

    flask的ORM操作 目录 flask的ORM操作 ORM Flask SQLAlchemy扩展 数据模型 模型之间的关联 表管理 操作数据 新增 修改 删除 事务 查询 Flask SQLAlchemy提供了分页方法 四 文件的迁移 f