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)
代码刨析
在这个示例中,我们定义了两个类:
User
和Article
,并建立了一对多的关系:
User
类拥有一个属性articles
,它是一个关系属性,通过relationship
函数来定义。这个关系会映射到Article
类,并且通过back_populates
参数指定了反向关系的属性名为author
。Article
类中有一个属性author
,也是一个关系属性,通过relationship
函数定义。它映射到User
类,并且通过back_populates
参数指定了反向关系的属性名为articles
。在这个示例中,我们通过
relationship
来定义了一对多关系,并且通过ForeignKey
来建立了外键关系,将Article
表中的author_id
与User
表中的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()
代码刨析
User
类中包括了一个关联属性news
,它通过relationship
来指明与News
类的关系。而在News
类中,使用了ForeignKey
来建立了对User
表的外键关联,并通过relationship
指定了与User
类的关联关系。create_data
函数创建了一个用户和两条新闻,并将它们添加到数据库中。query_data
函数中,你首先查询了一条新闻,然后获取了它的uid
(用户ID),接着通过用户ID查询了相应的用户。query_data2
函数中,首先查询了一条新闻,然后通过news1.user
直接访问了与之关联的用户对象。query_data3
函数中,首先查询了一个用户,然后通过user1.news
直接访问了与之关联的新闻对象。