SQLAlchemy关联表一对多关系的详解

简介: SQLAlchemy关联表一对多关系的详解

ORM关系之一对多

mysql级别的外键,还不够爽,必须拿到一个表的外键,然后通过这个外键再去另外一张表中查找,这样太麻烦了。

SQLAlchemy提供了一个relationship,这个类可以定义属性,以后在访问相关联的表的时候就直接可以通过属性访问的方式就可以访问得到了。

另外,可以通过backref来指定反向访问的属性名称。newss是指有多篇新闻。他们之间的关系是一个“一对多”的关系。

数据库层面

在数据库中,一对多关系通常通过在多的一方表中添加一个外键列,该列引用了另一方的主键。例如,在一个博客系统中,一个用户可以有多篇文章,那么在文章表中通常会包含一个指向用户表的外键。

ORM 层面

在 ORM 中,一对多关系通常通过在一个类中定义一个关联属性来实现。这个关联属性指明了与之相关联的类,以及在数据库中如何表示这种关系。

在 SQLAlchemy 中,可以使用 relationship 来定义一对多关系。比如:

class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    username = Column(String, unique=True, nullable=False)
    articles = relationship('Article', back_populates='author', cascade='all, delete-orphan')
class Article(Base):
    __tablename__ = 'articles'
    id = Column(Integer, primary_key=True)
    title = Column(String, nullable=False)
    content = Column(String, nullable=False)
    author_id = Column(Integer, ForeignKey('users.id'))
    author = relationship('User', back_populates='articles')

查询操作

在一对多关系中,可以轻松地通过关联属性来访问相关联的对象。例如,你可以通过 user.articles 访问一个用户的所有文章,或者通过 article.author 访问一篇文章的作者。

# 获取第一个用户的所有文章
user = session.query(User).first()
articles = user.articles
# 获取第一篇文章的作者
article = session.query(Article).first()
author = article.author

添加与删除

在一对多关系中,你可以通过调用关联属性来添加新的对象。例如,你可以通过 user.articles.append(new_article) 将一篇新文章添加到一个用户的文章列表中。

# 创建一个新的文章,并将其关联到第一个用户
new_article = Article(title='New Article', content='Content of the new article')
user.articles.append(new_article)
session.commit()

还可以通过 user.articles.remove(article) 删除一个用户的某篇文章。

# 删除用户的第一篇文章
article_to_delete = user.articles[0]
user.articles.remove(article_to_delete)
session.commit()

以下是一个简单的示例,使用 SQLAlchemy 来创建一个用户和文章的一对多关系:

示例1

from sqlalchemy import create_engine, Column, Integer, String, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
Base = declarative_base()
class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    username = Column(String, unique=True, nullable=False)
    articles = relationship('Article', back_populates='author', cascade='all, delete-orphan')
class Article(Base):
    __tablename__ = 'articles'
    id = Column(Integer, primary_key=True)
    title = Column(String, nullable=False)
    content = Column(String, nullable=False)
    author_id = Column(Integer, ForeignKey('users.id'))
    author = relationship('User', back_populates='articles')
# 创建数据库连接
engine = create_engine('sqlite:///example.db')
# 创建表格
Base.metadata.create_all(engine)

代码刨析

在这个示例中,我们定义了两个类:UserArticle,并建立了一对多的关系:

  • User 类拥有一个属性 articles,它是一个关系属性,通过 relationship 函数来定义。这个关系会映射到 Article 类,并且通过 back_populates 参数指定了反向关系的属性名为 author
  • Article 类中有一个属性 author,也是一个关系属性,通过 relationship 函数定义。它映射到 User 类,并且通过 back_populates 参数指定了反向关系的属性名为 articles

在这个示例中,我们通过 relationship 来定义了一对多关系,并且通过 ForeignKey 来建立了外键关系,将 Article 表中的 author_idUser 表中的 id 关联起来。

这样,一个用户可以拥有多篇文章,而每篇文章只属于一个用户。

示例2

from sqlalchemy import Column,Integer,String,Text,ForeignKey
from sqlalchemy.orm import relationship
from db_util import Base,Session
class User(Base):
  __tablename__ = 't_user'
  id = Column(Integer,primary_key=True,autoincrement=True)
  uname = Column(String(50),nullable=False,name='name')
  # news = relationship('News')  # 不友好
  def __repr__(self):
    return f'<User: id={self.id} uname={self.uname}>'
# 1对多 ForeignKey的关键字要建立在 多一边
class News(Base):
  __tablename__ = 't_news'
  id = Column(Integer,primary_key=True,autoincrement=True)
  title = Column(String(50),nullable=False)
  content = Column(Text,nullable=False)
  uid = Column(Integer,ForeignKey('t_user.id'))
  user = relationship('User',backref='news') # 将主表的数据注入到这个字段
  def __repr__(self):
    return f'<News: id={self.id} title={self.title} content={self.content} uid={self.uid}>'
def create_data():
  user = User(uname = 'sxt') 
  news1 = News(title='Python',content='flask',uid = 1)
  news2 = News(title='MySQL',content='SQL',uid = 1)
  with Session() as ses:
    ses.add(user)
    ses.commit()
  with Session() as ses:
    ses.add(news1)
    ses.add(news2)
    ses.commit()
def query_data():
  with Session() as ses:
    # news1 = ses.query(News).first()
    # print(news1)
    # select u.id u.uname from t_news n left join t_user u n.uid = u.id where n.id = 1;
    news1 = ses.query(News).first()
    uid = news1.uid
    user = ses.query(User).first()
    print(user)
def query_data2():
  # 通地子表查询主表的数据
  with Session() as ses:
    news1 = ses.query(News).first()
    print(news1.user)
def query_data3():
  # 通地主表查找子表的数据
  with Session() as ses:
    user1 = ses.query(User).first()
    print(user1.news)
if __name__ == '__main__':
  # Base.metadata.create_all()
  # create_data()
  # query_data()
  # query_data2()
  query_data3()

代码刨析

  1. User 类中包括了一个关联属性 news,它通过 relationship 来指明与 News 类的关系。而在 News 类中,使用了 ForeignKey 来建立了对 User 表的外键关联,并通过 relationship 指定了与 User 类的关联关系。
  2. create_data 函数创建了一个用户和两条新闻,并将它们添加到数据库中。
  3. query_data 函数中,你首先查询了一条新闻,然后获取了它的 uid(用户ID),接着通过用户ID查询了相应的用户。
  4. query_data2 函数中,首先查询了一条新闻,然后通过 news1.user 直接访问了与之关联的用户对象。
  5. query_data3 函数中,首先查询了一个用户,然后通过 user1.news 直接访问了与之关联的新闻对象。
相关文章
|
Web App开发 应用服务中间件 Go
尝鲜:如何搭建一个简单的webrtc服务器
前几天我一朋友问我有关webrtc的事,简单了解了下相关知识,搭建了一个webrtc的服务,以及经历的各种踩坑事件,感觉踩坑主要是Python、Node、OpenSSL等版本问题和证书问题导致。本来以为很简单的搭建,但在搭建的过程中遇到各种阻碍,写一篇文章梳理一下。
12530 0
|
消息中间件 SQL 存储
超详细的RabbitMQ入门,看这篇就够了!
RabbitMQ入门,看这篇就够了
216996 68
|
JavaScript 数据库 Python
一篇文章搞懂flask_sqlalchemy常用操作
这篇文章介绍了Flask框架中SQLAlchemy库的常用操作,包括查询、删除和更新数据的方法和技巧。
658 3
|
6月前
|
存储 人工智能 监控
一键部署 Dify + MCP Server,高效开发 AI 智能体应用
本文将着重介绍如何通过 SAE 快速搭建 Dify AI 研发平台,依托 Serverless 架构提供全托管、免运维的解决方案,高效开发 AI 智能体应用。
5843 65
|
缓存 Linux 开发工具
CentOS 7- 配置阿里镜像源
阿里镜像官方地址http://mirrors.aliyun.com/ 1、点击官方提供的相应系统的帮助 :2、查看不同版本的系统操作: 下载源1、安装wget yum install -y wget2、下载CentOS 7的repo文件wget -O /etc/yum.
255329 0
|
7月前
|
存储 负载均衡 算法
基于 C++ 语言的迪杰斯特拉算法在局域网计算机管理中的应用剖析
在局域网计算机管理中,迪杰斯特拉算法用于优化网络路径、分配资源和定位故障节点,确保高效稳定的网络环境。该算法通过计算最短路径,提升数据传输速率与稳定性,实现负载均衡并快速排除故障。C++代码示例展示了其在网络模拟中的应用,为企业信息化建设提供有力支持。
181 15
|
10月前
|
JSON 安全 API
Python调用API接口的方法
Python调用API接口的方法
1378 5
|
XML JSON 关系型数据库
PostgreSQL支持多种数据类型
PostgreSQL支持多种数据类型
743 2
|
12月前
|
XML JSON Ubuntu
Python实用记录(十五):PyQt/PySide6打包成exe,精简版(nuitka/pyinstaller/auto-py-to-exe)
本文介绍了使用Nuitka、PyInstaller和auto-py-to-exe三种工具将Python的PyQt/PySide6应用打包成exe文件的方法。提供了详细的安装步骤、打包命令和参数说明,适合新手学习和实践。
3455 0
|
API 数据库 Python
Python web框架fastapi数据库操作ORM(二)增删改查逻辑实现方法
Python web框架fastapi数据库操作ORM(二)增删改查逻辑实现方法
685 1