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 直接访问了与之关联的新闻对象。
相关文章
|
4月前
gorm 多对多关系 以及 关联的操作
gorm 多对多关系 以及 关联的操作
55 0
|
7月前
|
SQL XML 数据格式
mybatis-关联关系&一对多关联&多对一关联&多对多
mybatis-关联关系&一对多关联&多对一关联&多对多
|
8月前
|
SQL 数据库 Python
根据外键倒查关联表
根据外键倒查关联表
|
8月前
|
C语言
一对多表操作
一对多表操作
43 0
|
Java 数据库
不设外键:用程序来实现表之间的关联
Association 相信有很多同学都有使用过Hibernate框架的开发经验,尽管现在开发人员对Hibernate的褒贬不一,我们暂且不谈这个话题。
1712 1
|
Java 数据库连接 关系型数据库
|
Java 数据库连接 关系型数据库
|
Python 项目管理 网络安全