自动化运维Python系列之ForeignKey、relationship联表查询

一对多和多对多

数据库表结构设计是程序项目开发前的重要环节,后期数据库操作都是围绕着这个已经设计好的表结构进行,如果表结构设计有问题,整个程序项目就有存在需要整个推翻重构的风险...

数据库表结构除了简单的单表操作以外,还有一对多、多对多等。

一对多

基于SQLAlchemy我们可以先创建如下结构的2张表,然后来看看具体怎样通过外键ForeignKey或者relationship联表操作

创建表

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import sessionmaker, relationship
from sqlalchemy import create_engine
 
engine = create_engine("mysql+pymysql://root:[email protected]:3306/s13?charset=utf8",
                                                                    max_overflow=5)
 
Base = declarative_base()
 
# 创建用户组表
class Group(Base):
    __tablename__ = ‘group‘
    nid = Column(Integer, primary_key=True, autoincrement=True)
    caption = Column(String(32))
 
# 创建用户表
class User(Base):
    __tablename__ = ‘user‘
    nid = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(32))
    # 添加了外键 表示再增加用户时 group_id 只能是表group中存在的id 否则报错
    group_id = Column(Integer, ForeignKey(‘group.nid‘))
 
def init_db():
    Base.metadata.create_all(engine)
 
def drop_db():
    Base.metadata.drop_all(engine)
 
# init_db执行创建表操作
# init_db()
 
# 实例化类 开始后续的查询操作
Session = sessionmaker(bind=engine)
session = Session()
 
-------------- 插入数据 ---------------------
# 插入组名
# session.add(Group(caption=‘运维‘))
# session.add(Group(caption=‘开发‘))
# session.commit()
 
# 插入用户
# session.add_all([
#     User(name=‘user_01‘, group_id=1),
#     User(name=‘user_02‘, group_id=1),
#     User(name=‘user_03‘, group_id=2),
# ])
# session.commit()

__repr__

单表查询结果获取的是一个内存对象,我们可以在表中增加一个特殊对象方法__repr__,自定义查询显示结果

# 获得的只是个对象
ret = session.query(User).filter(User.name == ‘user_01‘).all()
print(ret)
 
# 输出
[<day13.s1.User object at 0x00000288737C1898>]
 
# 在User表中增加__repr__方法 查询后会自动执行
class User(Base):
    __tablename__ = ‘user‘
    nid = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(32))
    group_id = Column(Integer, ForeignKey(‘group.nid‘))
 
    # __repr__方法会自动执行
    def __repr__(self):
        temp = ‘%s  %s %s‘ %(self.nid, self.name, self.group_id)
        return temp
 
# 再次执行后结果
[1  User_01 1]
 
obj = ret[0]
print(obj.nid, obj.name, obj.group_id)
# 输出
1 User_01 1

# 仅查用户名
ret = session.query(User.name).all()
print(ret)
# 输出
[(‘User_01‘,), (‘User_02‘,), (‘User_03‘,)]

联表查询

# 联表查询原理是自动给你生成sql语句然后执行
sql = session.query(User).join(Group)
print(sql)
ret = session.query(User).join(Group).all()
print(ret)
 
# left join
# ret = session.query(User).join(Group, isouter=True).all()
# print(ret)
 
# 输出
SELECT user.nid AS user_nid, user.name AS user_name, user.group_id AS
user_group_id 
FROM user INNER JOIN `group` ON `group`.nid = user.group_id
[1  user_01 1, 2  user_02 1, 3  user_03 2]

指定映射关系

# 指定映射关系联表查询
ret = session.query(User.name, Group.caption).join(Group).all()
print(ret)
 
# 输出
[(‘user_01‘, ‘运维‘), (‘user_02‘, ‘运维‘), (‘user_03‘, ‘开发‘)]

relationship 正向查找

为了简化联合查询,我们还可以创建一个2个表之间的虚拟关系relationship,该关系与表结构无关,仅方便我们后续查询

class User(Base):
    __tablename__ = ‘user‘
    nid = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(32))
    # 外键
    group_id = Column(Integer, ForeignKey(‘group.nid‘))
    # 创建虚拟关系 relationship 一般与外键配合使用
    group = relationship("Group", backref=‘uuu‘)
 
    def __repr__(self):
        temp = ‘%s  %s %s‘ %(self.nid, self.name, self.group_id)
        return temp
ret = session.query(User).all()
for obj in ret:
    # obj 代表 User
    # group 代表新 Group
    print(obj.nid, obj.name, obj.group_id, obj.group.nid, obj.group.caption)
 
# 输出
1 user_01 1 1 运维
2 user_02 1 1 运维
3 user_03 2 2 开发

relationship 反向查找

需求:查找表2中的所有事运维职位的人

# 查所有是 运维 的人
ret = session.query(User.name, Group.caption).join(Group, isouter=True).
                   filter(Group.caption == ‘运维‘)).all()                                                                      
print(ret)
 
# 利用relationship 新方式反向查询
obj = session.query(Group).filter(Group.caption == ‘运维‘).first()
print(obj.nid, obj.caption)
# uuu 代表在这个组下面的所有人 是一个列表
print(obj.uuu)
 
# 输出
[(‘user_01‘, ‘运维‘), (‘user_02‘, ‘运维‘)]
1 运维
[1  user_01 1, 2  user_02 1]

多对多

例如公司里的很多服务器同时有包括root用户在内的很多账户可以登陆,就是一个简单的多对多结构

老方法查找 C2 主机有哪些账户可以登陆

# 1、找到 hostname 为c1的nid
host_obj = session.query(Host).filter(Host.hostname == ‘c2‘).first()
print(host_obj.nid)
 
# 2、指定映射关系查找 对应主机用户ID
host_to_host_user = session.query(HostToHostUser.host_user_id).filter
#                         (HostToHostUser.host_id == host_obj.nid).all()
print(host_to_host_user)
 
# [(1,), (2,), (3,)]
# [1, 2, 3]
 
r = zip(*host_to_host_user)
 
# 3、查找到用户
users = session.query(HostUser.username).filter(HostUser.nid.in_(list(r)[0])).all()
print(users)
 
# 输出
1
[(1,), (3,)]
[(‘root‘,), (‘db‘,)]

利用 relationship 关系简化多对多查询

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import sessionmaker, relationship
from sqlalchemy import create_engine
 
engine = create_engine("mysql+pymysql://root:[email protected]:3306/s13",
                                                         max_overflow=5)
 
Base = declarative_base()
 
class Host(Base):
    __tablename__ = ‘host‘
    nid = Column(Integer, primary_key=True, autoincrement=True)
    hostname = Column(String(32))
    port = Column(String(32))
    ip = Column(String(32))
 
class HostUser(Base):
    __tablename__ = ‘host_user‘
    nid = Column(Integer, primary_key=True, autoincrement=True)
    username = Column(String(32))
 
class HostToHostUser(Base):
    __tablename__ = ‘host_to_host_user‘
    nid = Column(Integer, primary_key=True, autoincrement=True)
 
    host_id = Column(Integer, ForeignKey(‘host.nid‘))
    host_user_id = Column(Integer, ForeignKey(‘host_user.nid‘))
 
    # 建立关系
    host = relationship(‘Host‘, backref=‘h‘)
    host_user = relationship(‘HostUser‘, backref=‘u‘)
 
def init_db():
    Base.metadata.create_all(engine)
 
def drop_db():
    Base.metadata.drop_all(engine)
 
# 创建表
# init_db()
 
# 插入数据
Session = sessionmaker(bind=engine)
session = Session()
 
# session.add_all([
#     Host(hostname=‘c1‘, port=‘22‘, ip=‘1.1.1.1‘),
#     Host(hostname=‘c2‘, port=‘22‘, ip=‘1.1.1.2‘),
# ])
# session.commit()
 
# session.add_all([
#     HostUser(username=‘root‘),
#     HostUser(username=‘sa‘),
#     HostUser(username=‘db‘),
# ])
# session.commit()
 
# session.add_all([
#     HostToHostUser(host_id=1, host_user_id=1),
#     HostToHostUser(host_id=1, host_user_id=2),
#     HostToHostUser(host_id=2, host_user_id=1),
#     HostToHostUser(host_id=2, host_user_id=3),
# ])
# session.commit()
 
# relationship 多对多查询
host_obj = session.query(Host).filter(Host.hostname == ‘c2‘).first()
for item in host_obj.h:
    print(item.host_user.username)
 
# 输出
root
db

另一种简单方式

from sqlalchemy import create_engine,and_,or_,func,Table
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String,ForeignKey
from sqlalchemy.orm import sessionmaker,relationship
 
engine = create_engine("mysql+pymysql://root:[email protected]:3306/s13",
                                                          max_overflow=5)
 
Base = declarative_base()
 
class HostToHostUser(Base):
    __tablename__ = ‘host_to_host_user‘
    nid = Column(Integer, primary_key=True, autoincrement=True)
 
    host_id = Column(Integer, ForeignKey(‘host.nid‘))
    host_user_id = Column(Integer, ForeignKey(‘host_user.nid‘))
 
class Host(Base):
    __tablename__ = ‘host‘
    nid = Column(Integer, primary_key=True, autoincrement=True)
    hostname = Column(String(32))
    port = Column(String(32))
    ip = Column(String(32))
    
    # 在其中一张表中加如下secondary
    host_user = relationship(‘HostUser‘, secondary=HostToHostUser.__table__,
                                                                   backref=‘h‘)
 
class HostUser(Base):
    __tablename__ = ‘host_user‘
    nid = Column(Integer, primary_key=True, autoincrement=True)
    username = Column(String(32))
 
Session = sessionmaker(bind=engine)
session = Session()
 
host_obj = session.query(Host).filter(Host.hostname == ‘c2‘).first()
print(host_obj.host_user)
for item in host_obj.host_user:
    print(item.username)
 
# 输出
[<__main__.HostUser object at 0x000001E44AF49390>, <__main__.HostUser object at 
0x000001E44AF493C8>]
root
db
时间: 2024-10-02 06:02:42

自动化运维Python系列之ForeignKey、relationship联表查询的相关文章

自动化运维Python系列(一)之基础篇

Python介绍 Python是由创始人吉多·范罗苏姆(Guido van Rossum)在1989年圣诞节假期期间,为了打发时间,构思出来的一个新的脚本解释器.由于Guido在开发Python语言过程中,借鉴了很多ABC语言特性,所有后来包括Guido自己也那么认为,Python语言的前身就是ABC语言. Python是一门面向对象的.动态解释型强定义语言:Python崇尚简洁.优美.清晰,是一门优秀的被广泛使用的语言. 在2015年以前,最流行的Python版本还是2.4,但是由于Pytho

自动化运维Python系列(七)之Socket编程

了解知识点TCP\IP 要想理解socket首先得熟悉一下TCP/IP协议族, TCP/IP(Transmission Control Protocol/Internet Protocol)即传输控制协议/网间协议,定义了主机如何连入因特网及数据如何再它们之间传输的标准, 从字面意思来看TCP/IP是TCP和IP协议的合称,但实际上TCP/IP协议是指因特网整个TCP/IP协议族.不同于ISO模型的七个分层,TCP/IP协议参考模型把所有的TCP/IP系列协议归类到四个抽象层中(数据链路层和物理

自动化运维Python系列(四)之装饰器和生成器

装饰器 在理解什么事装饰器之前,我们需要理解:函数也是一个对象,可以赋值给变量,通过变量来调用 def f1():     print('2016') d = f1 d() 输出: 2016 那么装饰器的作用就是在不改变原函数的前提下,调用这些函数,并且为函数增加我们需要的新功能. 我们平时在编写好很多独立函数模块以后,突然需要在每个模块内添加一个功能,比如: def f1():     print('F1') def f2():     print('F2') def f3():     pr

自动化运维Python系列之Django进阶操作

FBV && CBV FBV.CBV是Django视图路由处理模型,当用户请求送达路由系统URL后,由其转发给视图view来分析并处理 // FBV    function base views  // CBV    class base views 区别就是一个直接用函数驱动,一个用类驱动,两者在使用上存在一些区别 1)FBV URL中根据路由匹配直接转发给视图中的某一个处理函数 urlpatterns = [     url(r'^home/', views.home), ] 视图函数

自动化运维Python系列之Django信号、缓存操作

Django信号 Django内部提供了一种"信号强度"处理机制,简单理解就是当Django在接收到请求后内部做某些特定操作前发出信号,提醒一些接受者或者做操作,这样的好处就是方便程序定制小功能插件,也是对本身框架的一种节藕操作 1)Django的内置信号 Model signals     pre_init                # django的modal执行其构造方法前,自动触发     post_init               # django的modal执行其构

自动化运维Python系列之消息队列RabbitMQ

RabbitMQ RabbitMQ是一个由erlang开发的AMQP(Advanced Message Queue )的开源实现.AMQP 的出现其实也是应了广大人民群众的需求,虽然在同步消息通讯的世界里有很多公开标准(如 COBAR的 IIOP ,或者是 SOAP 等),但是在异步消息处理中却不是这样,只有大企业有一些商业实现(如微软的 MSMQ ,IBM 的 Websphere MQ 等),因此,在 2006 年的 6 月,Cisco .Redhat.iMatix 等联合制定了 AMQP 的

自动化运维Python系列之Memcache、Redis操作

Memcached Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载.它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高动态.数据库驱动网站的速度.Memcached基于一个存储键/值对的hashmap.其守护进程(daemon )是用C写的,但是客户端可以用任何语言来编写,并通过memcached协议与守护进程通信. Memcached安装 wget http://memcached.org/latest tar -zxvf memcach

自动化运维Python系列(六)之面向对象

面向对象编程 面向过程:根据业务逻辑从上到下垒代码 函数式:将某功能代码封装到函数中,以后直接调用,不需要再次编写 面向对象:对函数进行分类和封装,让开发"更快更好更强..." # 像Java和C#等编程语言仅支持面向对象编程,而Python支持函数式编程和面向对象编程混用 面向对象示例 # 函数式编程 def bar():     print('bar')   bar()  # 直接调用函数 # 面向对象编程 class Foo:  # 创建类        def bar(self

自动化运维Python系列(三)之基础函数和文件操作

函数作用 增强代码的重用性和可读性 在没有使用函数编程之前,我们可能一直遵循的都是面向过程编程,即根据业务逻辑从上到下实现各个功能,这样的做的坏处是代码可读性不强,大量冗余代码,而且执行效率不高:有了函数后,我们就可以将多次使用到的相同代码模块放在单独的函数定义中,在任何想要调用它的地方随时调用,这就叫做函数式编程. 面向对象编程其实就是对函数进行再分类和封装,让开发"更快更好更强..." 函数的定义 def 函数名(参数): ... 函数体 ... 返回值 函数的定义主要有如下要点: