Python sqlalchemy 库 relationship() 相关笔记:关联查询

su_ · 2023年05月24日 · 2865 次阅读

前言

在学习使用 SQLAlchemy 库进行数据库操作中使用 relationship 进行表表关联时,遇到的一些相关问题,记录一下结果。

relationship 函数

child_api = relationship("ApiSQL", backref="api", lazy='dynamic')
"""
第一个参数是你对应关联表的映射类名
我们主要还是看后两个参数:backref、lazy
  :backref  声明反向查询时的方法
  :lazy  关联查询时数据的加载方式,建议设置成“dynamic”,懒加载模式
"""

具体看代码:

model 文件

import enum
from datetime import datetime

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine, Column, Integer, String, Boolean, Enum, DateTime, ForeignKey, Table
from sqlalchemy.orm import sessionmaker, relationship

engine = create_engine("mysql+pymysql://root:123456@127.0.0.1:3306/test_api", echo=True)
Session = sessionmaker(bind=engine)
session = Session()

Base = declarative_base()


##############################
# 可新增用例样式SQL  开始
##############################
class ApiMethod(enum.Enum):
    """
    请求方式枚举模型
    """
    post = 1
    get = 2
    put = 3
    delete = 4


class ProjectSQL(Base):
    """
    项目表
    """
    __tablename__ = 'project'
    id = Column(Integer, primary_key=True, autoincrement=True)
    projectname = Column(String(255), unique=True, nullable=False, index=True, comment="项目名称")
    creator = Column(String(100), nullable=False, comment="创建人")
    state = Column(Boolean, default=True, comment='项目状态')
    child_api = relationship("ApiSQL", backref="api", lazy='dynamic')
    __table_args__ = ({'comment': "项目信息表"})

    def __repr__(self):
        id_ = self.id
        projectname = self.projectname
        state = self.state
        creator = self.creator
        data = {
            "id": id_,
            "projectname": projectname,
            "creator": creator
        }
        return f"{data}"


class ApiSQL(Base):
    """
    接口表
    """
    __tablename__ = 'api'
    id = Column(Integer, primary_key=True, autoincrement=True)
    api_name = Column(String(64), nullable=False, index=True, comment="接口名称")
    method = Column(Enum(ApiMethod), nullable=False, comment="请求方式:1-post,2-get,3-put,4-delete")
    url = Column(Boolean, nullable=False, comment="接口地址,不包含host跟端口")
    update_time = Column(DateTime, onupdate=datetime.now, default=datetime.now, comment="更新时间")
    headers = Column(String(255), comment="请求头")
    param = Column(String(255), comment="请求参数")
    project_id = Column(Integer, ForeignKey('project.id'), comment="对应项目的id")
    __table_args__ = ({'comment': "项目-接口表"})

    def __repr__(self):
        id = self.id
        api_name = self.api_name
        method = self.method
        url = self.url
        headers = self.headers
        param = self.param
        update_time = self.update_time
        return f"{{'id': {id}, 'api_name': {api_name}, 'method': {method}, 'url': {url},'header': {headers},'param': {param}, 'update_time': {update_time}}}"

正向查询运行:

pro = session.query(ProjectSQL).filter_by(id=2).first()

print(f"第一个打印出来的是主表数据本身: {pro}")
# child_api 是主表ProjectSQL中relationship字段的变量名
child_pro = pro.child_api   

print(f"第二个打印出来的是关联子表查询数据SQL: {child_pro}")
print("***************再依次打印子表对象类型************************")
print(type(child_pro))
print("***************************************")

print(f"第三个打印出来的是关联子表查询数据: {child_pro.all()}")

结果

第一个打印出来的是主表数据本身: {'id': 2, 'projectname': '我的项目', 'creator': '默认'}
第二个打印出来的是关联子表查询数据SQL: SELECT api.id AS api_id, api.api_name AS api_api_name, api.method AS api_method, api.url AS api_url, api.update_time AS api_update_time, api.headers AS api_headers, api.param AS api_param, api.project_id AS api_project_id 
FROM api 
WHERE %(param_1)s = api.project_id
***************再依次打印子表对象类型************************
<class 'sqlalchemy.orm.dynamic.AppenderQuery'>
***************************************
2023-05-25 09:04:12,839 INFO sqlalchemy.engine.Engine SELECT api.id AS api_id, api.api_name AS api_api_name, api.method AS api_method, api.url AS api_url, api.update_time AS api_update_time, api.headers AS api_headers, api.param AS api_param, api.project_id AS api_project_id 
FROM api 
WHERE %(param_1)s = api.project_id
2023-05-25 09:04:12,839 INFO sqlalchemy.engine.Engine [generated in 0.00012s] {'param_1': 2}
第三个打印出来的是关联子表查询数据: [{'id': 1, 'api_name': 1, 'method': ApiMethod.post, 'url': True,'header': 1,'param': 1, 'update_time': 2023-05-24 14:40:08, 'project_id': 2}, {'id': 2, 'api_name': 2, 'method': ApiMethod.get, 'url': True,'header': 2,'param': 2, 'update_time': 2023-05-24 14:40:20, 'project_id': 2}, {'id': 3, 'api_name': 3, 'method': ApiMethod.delete, 'url': True,'header': 3,'param': 3, 'update_time': 2023-05-24 14:40:38, 'project_id': 2}]

正向查询时,通过 relationship 函数来关联查询对应的关联表数据。但是反向查询时子表中并没有定义 relationship 函数字段,这时候参数 backref 字段的作用就体现出来了,反向查询时使用 .backref 参数值 来达到反向查询的效果,如下:

反向查询代码

child = session.query(ApiSQL).filter_by(id=2).first()

print(f"第一个打印出来的是子表数据本身: {child}")
# api 即在model映射表类主表ProjectSQL中relationship的backref参数值
project = child.api
print(f"第二个打印出来的是关联主表查询数据SQL: {project}")
print("***************再依次打印主表对象类型************************")
print(type(project))
print("***************************************")
print(f"第三个打印出来的是关联主表查询数据的具体字段: {project.projectname}")

结果

第一个打印出来的是子表数据本身: {'id': 2, 'api_name': 2, 'method': ApiMethod.get, 'url': True,'header': 2,'param': 2, 'update_time': 2023-05-24 14:40:20, 'project_id': 2}
2023-05-25 09:13:58,839 INFO sqlalchemy.engine.Engine SELECT project.id AS project_id, project.projectname AS project_projectname, project.creator AS project_creator, project.state AS project_state 
FROM project 
WHERE project.id = %(pk_1)s
2023-05-25 09:13:58,839 INFO sqlalchemy.engine.Engine [generated in 0.00009s] {'pk_1': 2}
第二个打印出来的是关联主表查询数据: {'id': 2, 'projectname': '我的项目', 'creator': '默认'}
***************再依次打印主表对象类型************************
<class 'model.api_case_sql.ProjectSQL'>
***************************************
第三个打印出来的是关联主表查询数据的具体字段: 我的项目

进程已结束,退出代码0


补充:以上例子是把 relationship 定义在主表中,当然也可以在子表中建立,这就看你实际的查询需求了,一般像例子中的一对多定义在主表中是比较合适的个人认为。

函数的 lazy 参数:

在上面的例子正向查询中我们可以看到打印出来的 child_pro 的结果是一串 SQL,其类型也是 AppenderQuery 类型,这个就是参数 lazy 的懒加载效果了,如果该字段是默认的话其查询结果就是查询出所有的数据,这样数据量比较大或者我们需要特殊的数据时就很难受,懒加载的话我们就可以跟按需去使用.all()、.filter_by() 等.函数来链接查询过滤数据了。
以上,谢谢阅读

暂无回复。
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册