SqlAlchemy使用教程(六) -- ORM 表间关系的定义与CRUD操作

在这里插入图片描述

  • SqlAlchemy使用教程(一) 原理与环境搭建
  • SqlAlchemy使用教程(二) 入门示例及编程步骤
  • SqlAlchemy使用教程(三) CoreAPI访问与操作数据库详解
  • SqlAlchemy使用教程(四) MetaData 与 SQL Express Language 的使用
  • SqlAlchemy使用教程(五) ORM API 编程入门

本章内容,稍微有些复杂,建议腾出2小时空闲时间,冲杯咖啡或泡杯茶 😃 , 慢慢看,在电脑上跑下代码,可以加深理解.

六、表间关系的定义与CRUD操作

表间关系主要包括:一对多,一对一,多对多。其中一对多关系中也隐含了多对一关系。
表间关系是数据库操作中的重要技术点,非常有必要理解与掌握。

1、 一对多表间关系的定义

一对多表间关系实现语法

以一对多关系为例,

  • 子表侧,与父表是一对多关系,
  • 父表侧,可以反向查询子表数据,与子表是多对一关系。
class Parent(Base):__tablename__ = "parent_table"id: Mapped[int] = mapped_column(primary_key=True)children: Mapped[List["Child"]] = relationship(back_populates="parent")class Child(Base):__tablename__ = "child_table"id: Mapped[int] = mapped_column(primary_key=True)parent_id: Mapped[int] = mapped_column(ForeignKey("parent_table.id"))parent: Mapped["Parent"] = relationship(back_populates="children")

说明:
1)在子表中添加外键字段,以及relationship()引用,
2)在父表中添加relationship()引用,用于反向查询。

示例代码

父表:company, 子表 person, 表结构类定义如下。

from sqlalchemy.orm import DeclarativeBase, Session
from sqlalchemy.orm import Mapped, mapped_column
from sqlalchemy.orm import relationship
from sqlalchemy import ForeignKey
from sqlalchemy import String, Integer
from typing import Listclass Base(DeclarativeBase):pass
class Company(Base):__tablename__ = "company"id: Mapped[int] = mapped_column(Integer, primary_key=True)company_name: Mapped[str] = mapped_column(String(30), index=True)persons: Mapped[List['Person']] = relationship(back_populates="company", cascade="all, delete-orphan")def __repr__(self) -> str:return f"Company(id={self.id}, company_name={self.company_name})"class Person(Base):__tablename__ = "person"id: Mapped[int] = mapped_column(Integer, primary_key=True)name: Mapped[str] = mapped_column(String(30))age: Mapped[int] = mapped_column(Integer)company_id: Mapped[int] = mapped_column(ForeignKey("company.id"))company: Mapped['Company'] = relationship(back_populates="persons")def __repr__(self) -> str:return f"Person(id={self.id}, name={self.name})"

说明:
1)从子表视角看,1个人只属于1个Company; 但1个Company 对应多个人,因此在父表则,relationship() 左侧的类型注解为 List[‘Person’], 也可以用Set[‘Person’]
2)父表中添加删除依赖,cascade=“all, delete-orphan”,即子表中不存在对父表记录的引用时,才能删除,以保证数据的完整性。
3)当前版本可能存在bug, 官方文档中的示例中有的字段使用简化写法(右侧未给出mapped_column()),sqlite3运行是没有问题的,但mysql, postgresql创建表时会丢弃简化写法的字段,导致后续insert等操作失败。 因此请严格请勿采有简化写法。

多对一关系的实现语法

当不需要反向查询时,则父表与子表形成Many to One 多对一关系, 在父表则添加子表的外键与relationship()引用,子表无须做额外配置

class Parent(Base):__tablename__ = "parent_table"id: Mapped[int] = mapped_column(primary_key=True)child_id: Mapped[int] = mapped_column(ForeignKey("child_table.id"))child: Mapped["Child"] = relationship()class Child(Base):__tablename__ = "child_table"id: Mapped[int] = mapped_column(primary_key=True)

如果允许 child_id空值,则将改字段的类型注解修改为

from typing import Optional
......
child_id: Mapped[Optional[int]] = mapped_column(ForeignKey("child_table.id"))

对于3.10+版本,类型注解支持 | 操作符

child_id: Mapped[int | None] = mapped_column(ForeignKey("child_table.id"))
child: Mapped[Child | None] = relationship(back_populates="parents")

2、 插入数据与多表联合查询

1)在数据库创建表

# 创建数据库连接引擎对象
engine = create_engine("mysql+mysqlconnector://root:Admin&123@localhost:3306/testdb")
# 将DDL语句映射到数据库表,如果数据库表不存在,则创建该表
Base.metadata.create_all(engine)
# 打印创建创建的表
Print(Base.metadata.tables) 

2)插入数据

基本步骤包括:

(1)为测试方便,先写1个get_or_create()函数,如果插入对象在数据库中已存在则不插入,以方便连续测试。
(2)创建session对象
(3)先创建父表对象并插入
(4)创建子表对象并插入
(5)用多表查询方法检查结果

Step-1: 自定义create_or_create()函数

官方提供的upsert方法不通用。下面函数是通用的,

def get_or_create(session, model, defaults=None, **kwargs):"""如果不存在则创建,如果存在则返回输入参数:session: sqlalchemy sessionmodel: 自定义的ORM类defaults: 有默认值的字段kwargs: 其他字段(必须包含主要字段)返回值:instance: 返回的实例"""instance = session.query(model).filter_by(**kwargs).first()if instance:print("instance already exists", instance)return instanceelse:params = dict((k, v) for k, v in kwargs.items()if not isinstance(v, ClauseElement))if defaults:params.update(defaults)instance = model(**params)session.add(instance)session.commit()print("instance inserted", instance)return instance
Step-2: 向两个关联表插入数据

用with 语句创建session对象,插入操作顺序,先父表再子表

with Session(engine) as session:# 插入数据get_or_create(session, Company, company_name="蜀汉")get_or_create(session, Company, company_name="曹魏")get_or_create(session, Company, company_name="东吴")stmt = select(Company)results = session.scalars(stmt)print(results.all())# insert data in person tablecompany_shu = session.scalars(select(Company).where(Company.company_name == "蜀汉")).first()get_or_create(session, Person, name="刘备",age=42, company=company_shu)get_or_create(session, Person, name="关羽",age=40, company=company_shu)get_or_create(session, Person, name="张飞", age=38, company=company_shu)company_wei = session.scalars(select(Company).where(Company.company_name == "曹魏")).first()get_or_create(session, Person, name="张辽", age=40, company=company_wei)get_or_create(session, Person, name="曹操", age=38, company=company_wei)company_wu = session.scalars(select(Company).where(Company.company_name == "东吴")).first()get_or_create(session, Person, name="周瑜", age=30, company=company_wu)

3) 多表联合查询

   # select with Join 多表查询stmt = select(Person).join(Person.company).where(Company.company_name == "蜀汉").order_by(Person.age)results = session.scalars(stmt)# 遍历结果for r in results:print(r.name, r.age, r.company.company_name)

Output:

instance inserted Company(id=1, company_name=蜀汉)
instance inserted Company(id=2, company_name=曹魏)
instance inserted Company(id=3, company_name=东吴)
[Company(id=1, company_name=蜀汉), Company(id=2, company_name=曹魏), Company(id=3, company_name=东吴)]
instance inserted Person(id=1, name=刘备)
instance inserted Person(id=2, name=关羽)
instance inserted Person(id=3, name=张飞)
instance inserted Person(id=4, name=张辽)
instance inserted Person(id=5, name=曹操)
instance inserted Person(id=6, name=周瑜)
张飞 38 蜀汉
关羽 40 蜀汉
刘备 42 蜀汉

删除数据
当删除父表记录时,子表中应无对此数据的引用,否则无法删除。

3、 一对一关系

从外键角度看,一对一关系也是一对多关系。实现时,

  • 在父表中收集子表数据时,类型注解不使用集合类型即可。
  • 子表中relationship()方法中添加single_parent=True.
class Parent(Base):__tablename__ = "parent_table"id: Mapped[int] = mapped_column(primary_key=True)
child: Mapped["Child"] = relationship(back_populates="parent")   # 一对多时,使用child: Mapped[List["Child"]] class Child(Base):__tablename__ = "child_table"id: Mapped[int] = mapped_column(primary_key=True)parent_id: Mapped[int] = mapped_column(ForeignKey("parent_table.id"))parent: Mapped["Parent"] = relationship(back_populates="child",single_parent=True)

4、 多对多关系

多对多关系特点:
1)父表与子表,子表与父表之间均为多对多关系。
2)通常使用1张中间表, 与父表、子表均实现1对多关系。
(当然有的ORM模型将中间表的创建隐藏起来,但在数据库中还是可以看到)

多对多关系定义示例

from __future__ import annotationsfrom sqlalchemy import Column
from sqlalchemy import Table
from sqlalchemy import ForeignKey
from sqlalchemy import Integer
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import relationshipclass Base(DeclarativeBase):pass# note for a Core table, we use the sqlalchemy.Column construct,
# not sqlalchemy.orm.mapped_column
association_table = Table("association_table",Base.metadata,Column("left_id", ForeignKey("left_table.id")),Column("right_id", ForeignKey("right_table.id")),
)class Parent(Base):__tablename__ = "left_table"id: Mapped[int] = mapped_column(primary_key=True)children: Mapped[List[Child]] = relationship(secondary=association_table, back_populates="parents")class Child(Base):__tablename__ = "right_table"id: Mapped[int] = mapped_column(primary_key=True)parents: Mapped[List[Parent]] = relationship(secondary=association_table, back_populates="children")

多对多关系的查询、插入操作与一对多查询相似。 需要注意的是删除操作。

从多对多关系中删除数据

用SQL来实现时,需要先从父表与子表删除数据,再从中间表删除。ORM API 可以自动完成这个过程。 如要删除子表的某条记录。

myparent.children.remove(somechild)

注:通过session.delete(somechild)时,MySql可能会报错,我遇到的原因有多种,不建议使用。
同样,如果要删除父表中的1条记录:

mychild.parent.remove(someparent) 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java链表(1)

🐵本篇文章将对单链表进行讲解,模拟实现单链表中常见的方法 一、什么是链表 链表是一种逻辑结构上连续而物理结构上不一定连续的线性表,链表由一个一个节点组成: 每一个节点中都由数据域(val)和指针域&…

实战EDA电子设计自动化经典入门模型VHDL代码编写(含代码解释)上篇--状态机,逻辑设计:Y=AB+C

前言 电子设计自动化(EDA): 定义:EDA是用于设计和开发复杂的电子系统(如集成电路)和印刷电路板的软件工具集合。这些工具通常用于设计电路、进行仿真测试、分析电路行为以及协助制造过程。应用:…

机器人学论文——智能施药机器人调研报告

目录 摘 要 Abstract 第一章:引言 1.1研究背景 1.2 研究意义 1.3文章架构 第二章:智能施药机器人发展现状 2.1引言 2.2 大田智能施药机器人发展现状 2.3 果园智能施药机器人发展现状 2.4 设施农业智能施药机器人发展现状 第三章:智能施药机器…

docker指令存档

目录 Docker 1、概念 2、架构图 3、安装 4、Docker怎么工作的? 5、Docker常用命令 帮助命令 镜像命令 1、查看镜像 2、帮助命令 3、搜索镜像 4、拉取镜像 5、删除镜像 容器命令 1、启动 2、查看运行的容器 3、删除容器 4、启动&停止 其他命令…

大数据数据流分析和处理的工具pig,从入门到精通!

介绍:Pig是一种数据流语言和运行环境,用于处理和分析大数据。 Pig由两个主要部分构成: Pig Latin语言:这是一种用于描述数据流的高级语言,它允许用户以较为简洁的方式编写数据处理和转换任务。 Pig执行环境&#xff1a…

C++ 模拟实现mapset

目录 一、改造红黑树 1、模板T改造节点 2、提取节点中的key 3、迭代器类 operator operator-- 4、改造insert 5、红黑树迭代器 6、 普通迭代器构造const迭代器 二、set 三、map 在stl中map和set的结构中,他们都使用一个红黑树进行封装。 由上图可知&a…

Java 枚举和注解

一、枚举类 把具体的对象一个一个例举出来的类就称为枚举类 枚举对应英文(enumeration, 简写 enum)枚举是一组常量的集合。可以这里理解:枚举属于一种特殊的类,里面只包含一组有限的特定的对象。 1.实现方式1——自定义类实现枚举 public class Enume…

Oracal学习

Oracle是什么 是甲骨文公司的一款支持事务且吞吐量高的数据库特点: (1)支持多用户、大事务量的事务处理 (2)数据安全性和完整性控制 (3)支持分布式数据处理 (4)可移植性…

【C++】C++入门基础讲解(一)

💗个人主页💗 ⭐个人专栏——C学习⭐ 💫点击关注🤩一起学习C语言💯💫 导读 经过一段时间的C语言学习,我们以及基本掌握了C语言的知识,今天,我们就开始学习C,…

2024最新幻兽帕鲁服务器多少钱一个?

幻兽帕鲁服务器多少钱?价格便宜,阿里云4核16G幻兽帕鲁专属服务器32元1个月、66元3个月,4核32G配置113元1个月、339元3个月;腾讯云4核16G14M服务器66元1个月、277元3个月、1584元一年。阿腾云atengyun.com分享阿里云和腾讯云palwor…

SpringBoot的自动装配原理

一、SpringBootConfiguration注解的作用 SpringBootApplication注解是SpringBoot项目的核心注解,加在启动引导类上。点击进去可以发现SpringBootApplication注解是一个组合注解。其中SpringBootConfiguration和EnableAutoConfiguration是由Spring提供的,剩下的注解是由JDK提供的…

简单计算与模拟1:鸡兔同笼(POJ 3237)

1 问题描述 图1 问题描述 2 解题思路 鸡有两只脚&#xff0c;兔子有四只脚&#xff0c;且输入数据可能为奇数&#xff0c;使用公式计算即可。 3 设计代码 #include <cstdio> int main() {int nCases, nFeets;while (scanf("%d", &nCases) ! EOF){for (in…

负载均衡-Ribbon-自定义负载均衡算法

1.Ribbon 是什么 SpringCloud Ribbon 是基于 Netflix Ribbon 实现的一套客户端负载均衡的工具主要功能是提供客户端的软件负载均衡算法&#xff0c;将 Netflix 的中间服务处连接在一起Ribbon的客户端组件提供一系列完整的配置项&#xff0c;如&#xff1a;连接超时、重试等。简…

echarts 绘制垂直滚动热力图

问题1&#xff1a;提示功能无效 问题2&#xff1a;值筛选无效 效果 在线浏览 下载echarts官网例子(heatmap Examples - Apache ECharts) 稍作改动&#xff1a; generateData 入参改为长度和宽度noise.perlin2(i / 40, j / 20) Math.random() * 5y轴倒置指定zlevel为2 通过定…

蓝桥杯备战——7.DS18B20温度传感器

1.分析原理图 通过上图我们可以看到DS18B20通过单总线接到了单片机的P14上。 2.查阅DS18B20使用手册 比赛的时候是会提供DS18B20单总线通讯协议的代码&#xff0c;但是没有提供读取温度数据的代码&#xff0c;所以还是需要我们去查看手册&#xff0c;我只把重要部分截下来了 …

python-自动篇-运维-根据计算机硬盘、主板、CPU生成注册信息

文章目录 准备代码效果 准备 本实例需要使用WMI模块&#xff0c;所以需要安装WMI模块。在安装WMI模块之前&#xff0c;先要安装pywin32模块&#xff0c;WMI模块需要win32api的支持。使用pip安装pywin32模块和WMI模块的代码如下&#xff1a; pip install win32com pip install …

Ubuntu 22.04 安装tomcat

tomcat是常用的Java服务容器,这篇文章我们就来讲讲如何安装它。 更新软件包 首先是更新软件包,这是最常规的操作 sudo apt update 然后是开始安装,不多一会就可以安装好了 sudo apt install tomcat9 然后看一下状态 sudo systemctl status tomcat9 发现虽然启动了,但…

Python第十一章(推导式)

一。列表推导式&#xff1a;目的化简代码&#xff0c;可以改变数据 例句1&#xff1a;如果while循环继续创建序列累加 例句2&#xff1a;如果for循环进行列表累加 1.循环列表推导式进行累加&#xff1a; 注释&#xff1a;第一个i表示的是返回的数据于列表&#xff0c;for后面内…

windows上使用anconda安装tensorrt环境

windows上使用anconda安装tensorrt环境 1 安装tensorrt1.1 下载最新的稳定的tensorrt 8.6.1(tensorrt对应的cuda、cudnn等版本是参考链接4)1.2 将tensorrt添加到环境变量1.3 安装tensorrt依赖1.4 安装Pycuda1.5 安装pytorch 2 测试2.1 测试TensorRT 样例(这个测试主要来源于参考…

【Python】01快速上手爬虫案例一:搞定豆瓣读书

文章目录 前言一、VSCodePython环境搭建二、爬虫案例一1、爬取第一页数据2、爬取所有页数据3、格式化html数据4、导出excel文件 前言 实战是最好的老师&#xff0c;直接案例操作&#xff0c;快速上手。 案例一&#xff0c;爬取数据&#xff0c;最终效果图&#xff1a; 一、VS…