首页 > 编程问答 >自动使用关系别名

自动使用关系别名

时间:2024-08-06 15:58:01浏览次数:12  
标签:python sqlalchemy alias

我有一个模式,其中包括自联接和多对多关系,如下所示:

from typing import List, Optional
from sqlalchemy import create_engine
from sqlalchemy.orm import (
    aliased,
    DeclarativeBase,
    Session,
    Mapped,
    mapped_column,
    relationship,
)
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.schema import ForeignKey
from sqlalchemy.types import String

class Base(DeclarativeBase):
    pass

class Post(Base):
    __tablename__ = 'post'
    id: Mapped[int] = mapped_column(primary_key=True)
    title: Mapped[str] = mapped_column(String(200), nullable=False)
    parent_id: Mapped[int] = mapped_column(ForeignKey('post.id'), nullable=True)
    parent: Mapped["Post"] = relationship('Post', foreign_keys=parent_id, remote_side=id)
    tags: Mapped[List['Tag']] = relationship('Tag', secondary='tag2post')

class Tag(Base):
    __tablename__ = 'tag'
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str] = mapped_column(String(100), nullable=False)

class Tag2Post(Base):
    __tablename__ = 'tag2post'
    id: Mapped[int] = mapped_column(primary_key=True)
    tag_id: Mapped[int] = mapped_column('tag_id', ForeignKey('tag.id'))
    tag: Mapped[Tag] = relationship(Tag, overlaps='tags')
    post_id: Mapped[int] = mapped_column('post_id', ForeignKey('post.id'))
    post: Mapped[Post] = relationship(Post, overlaps='tags')
    

engine = create_engine("sqlite+pysqlite:///:memory:", echo=True)
Base.metadata.create_all(engine)

with Session(engine) as session:
    session.add(tag_a := Tag(name='a'))
    session.add(tag_b := Tag(name='b'))
    session.add(parent := Post(title='parent', tags=[tag_a]))
    session.add(child := Post(title='child', parent=parent, tags=[tag_b]))

现在我想编写一个以下形式的查询“给我所有带有标签 a 并且其父级有一个标记 b ”。

我了解为每个步骤添加别名的“手动”方式:

    parent_alias = aliased(Post)
    parent_tag = aliased(Tag)
    parent_tag2post = aliased(Tag2Post)


    q = session.query(
        Post
    ).join(
        parent_alias, Post.parent_id == parent_alias.id
    ).join(
        parent_tag2post,
        parent_alias.id == parent_tag2post.post_id
    ).join(
        parent_tag,
        parent_tag2post.tag_id == parent_tag.id,
    ).join(
        Post.tags,
    ).filter(
        parent_tag.name == 'a',
        Tag.name == 'b',
    )
    print(q.one().title) # prints 'child'

但我正在处理更通用的代码,基本上是一种为我提供条件的查询语言:| ||将查询语言转换为联接的代码现在需要内省关系(如

  • Post.tags.name == 'b'
  • Post.parent.tags.name == 'a'

Post.tags Post.parents ),并在每个关系中用别名替换表并用别名重建联接条件...这听起来相当复杂且容易出错。 Post.parents.tags 有更简单的方法吗?也许,就像告诉 sqlalchemy 使用关系,但使用别名?或者是否有第三方软件包已经可以做到这一点?

Is there an easier way? Like, maybe, telling sqlalchemy to use a relationship, but with an alias? Or is there maybe a third-party package that already does this?


的确,手动添加别名来处理 SQLAlchemy 中复杂的关系可能会很繁琐且容易出错。幸运的是,SQLAlchemy 提供了一些机制可以简化这个过程。

1. 使用 any has 进行关联表查询

对于的查询“获取所有带有标签 'a' 且其父级带有标签 'b' 的帖子”,可以使用 SQLAlchemy 的 any has 操作符来简化代码,而无需手动创建别名:

from sqlalchemy import and_

q = session.query(Post).\
    filter(Post.tags.any(Tag.name == 'a')).\
    filter(Post.parent.has(Post.tags.any(Tag.name == 'b')))

print(q.one().title)  # prints 'child'
  • Post.tags.any(Tag.name == 'a') : 这部分检查 Post 是否与名称为 'a' 的 Tag 相关联。
  • Post.parent.has(Post.tags.any(Tag.name == 'b')) : 这部分检查 Post 的父级(也是一个 Post 对象)是否与名称为 'b' 的 Tag 相关联。

这种方式更加简洁易懂,因为它直接表达了查询的语义,而无需手动处理别名。

2. 使用 with_entities 加载关联数据

如果需要在查询结果中同时获取帖子及其关联的标签和父级信息,可以使用 with_entities 方法,并利用 SQLAlchemy 的自动别名功能:

q = session.query(Post).\
    options(
        joinedload(Post.parent).
        joinedload(Post.parent.tags)
    ).\
    filter(Post.tags.any(Tag.name == 'a')).\
    filter(Post.parent.has(Post.tags.any(Tag.name == 'b'))).\
    with_entities(Post, Post.parent, Tag)

results = q.all()
for post, parent, tag in results:
    print(f"Post: {post.title}, Parent: {parent.title if parent else None}, Tag: {tag.name}")
  • joinedload(Post.parent).joinedload(Post.parent.tags) : 使用 joinedload 可以一次性加载关联的父级及其标签,避免 N+1 查询问题。
  • with_entities(Post, Post.parent, Tag) : 指定要查询的实体,SQLAlchemy 会自动为关联的实体创建别名。

3. 使用第三方库:SQLAlchemy-Utils

如果需要更强大的查询构建功能,可以考虑使用第三方库,例如 SQLAlchemy-Utils。该库提供了一些工具来简化复杂查询的构建,包括自动别名管理。

总而言之,对于的情况,使用 any has 操作符结合 with_entities 以及 joinedload 可以更简洁地表达查询,而无需手动处理别名。如果需要更通用的查询构建机制,可以考虑使用 SQLAlchemy-Utils 等第三方库。

标签:python,sqlalchemy,alias
From: 78837861

相关文章

  • Python实现猜数字游戏:带提示范围和随机生成数字功能
    概述这篇文章将介绍一个使用Python编写的简单猜数字游戏。该游戏会随机生成一个1到10之间的数字,然后用户需要猜测这个数字。每次猜测后,程序会根据用户的输入调整提示范围,直到用户猜中为止。代码实现首先,导入必要的模块并生成一个随机数:importrandom#生成1-10之间的随机......
  • python SQLite 访问组件
    importosimportsqlite3fromsqlite3importErrorfromqueueimportQueue,EmptyfromtypingimportList,Tuple,AnyclassSQLiteDB:default_db_path=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))default_db_file=default_db_path......
  • Python 卡在第 1 页
    让它读取下一页结果的最佳方法是什么?目前正在拉取数据,但只有第1页importrequestsimportjsonpage=1url="https://api-prod.grip.events/1/container/4368/search?search=&sort=name&order=asc&type_id=4907,4906,5265,4964,4904,1026,4908&page=%d"headers={......
  • 无法反序列化解码 JWT python 时的关键数据
    我正在使用pyjwt库来解码JWT令牌。我在解码时遇到此错误。代码在文档中给出。importjwtencoded_jwt='''eyJ0eXAiOiJKV1QiLCJhbG......'''secret=b''''-----BEGINPUBLICKEY-----MIIFRjCCBC6gAwIBAgIQCIdSGhpikQCjOIY154XoqzANBgkqhkiG9......
  • Python科研武器库 - 字符串操作 - 字符串开头结尾判断 startswith() endswith()
    使用场景:按照开头的前缀、结尾的后缀来判断、筛选目标字符串。使用函数:str.startswith(search_string,start,end)str.endswith(search_string,start,end)search_string:要匹配的目标字符串。start:要从中匹配search_string的str的起始索引。end:要考虑匹配的str的结......
  • 我正在 python 中使用 aspose.pdf 将 pdf 转换为 excel 。但问题是它只能将 pdf 的前
    `从tkinter导入*将aspose.pdf导入为ap从tkinter导入文件对话框importpandasaspdinput_pdf=filedialog.askopenfilename(filetypes=(("PDF文件",".pdf"),("所有文件",".")))output_file=filedialog.asksaveasfil......
  • 如何在selenium python中访问电子邮件中的所有文件夹
    我使用imaplib库,但有时无法访问某些帐户,我使用poplib但它只支持访问主邮箱,但不支持访问其他文件夹,如垃圾邮件我想使用imaplib,但不会出现有时甚至无法访问的错误尽管我有一个帐户,但我仍然可以访问它,或者是否有另一个库可以快速支持该帐户?你想要使用Selenium和Python......
  • python安装torch-cluster、torch-scatter、torch-sparse和torch-geometric | torch_ge
    1.检查CUDA版本【方法1】用nvidia-smi已装cuda,用nvidia-smi或nvcc-V查看【方法2】用torch已装torch,用torch代码打印importtorchprint(torch.__version__)#查看pytorch安装的版本号print(torch.cuda.is_available())#查看cuda是否可......
  • Python:学生成绩管理系统(大学编程期末实验)
    引言在这个信息时代,教育管理的自动化已成为提高效率的关键。本文将介绍如何使用Python开发一个学生成绩管理系统,旨在简化成绩记录、查询和分析的过程。创作灵感来源本项目灵感来源于我在教育机构的工作经历,以及对提高教育管理效率的持续追求。通过复盘过往项目,我意识到一个......
  • 手把手教你使用Python网络爬虫下载一本小说(附源码)
    大家好,我是Python进阶者。前言前几天【磐奚鸟】大佬在群里分享了一个抓取小说的代码,感觉还是蛮不错的,这里分享给大家学习。一、小说下载如果你想下载该网站上的任意一本小说的话,直接点击链接进去,如下图所示。只要将URL中的这个数字拿到就可以了,比方说这里是951,那么这个数字......