sqlalchemy 一对多/多对一/多对多/一对一关系定义设置全方位操作方法

本文主要是介绍sqlalchemy 一对多/多对一/多对多/一对一关系定义设置全方位操作方法,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

sqlalchemy 作为一款ORM在操作数据库方面非常的方便,这里总结了一些对应关系的设置以及查询方法!

使用外键关联表:
表设计

from sqlalchemy import Column, ForeignKey
from sqlalchemy.types import String, Integer, CHAR, BIGINTclass Blog(BaseModel):__tablename__ = 'blog'id = Column(BIGINT, primary_key=True, autoincrement=True)title = Column(String(64), server_default='', nullable=False)text = Column(String, server_default='', nullable=False)user = Column(BIGINT, ForeignKey('user.id'), index=True, nullable=False)create = Column(BIGINT, index=True, server_default='0', nullable=False)class User(BaseModel):__tablename__ = 'user'id = Column(BIGINT, primary_key=True, autoincrement=True)name = Column(String(32), server_default='', nullable=False)username = Column(String(32), index=True, server_default='', nullable=True)password = Column(String(64), server_default='', nullable=False)

提交数据

session = Session()
user = User(name='first', username=u'新的')
session.add(user)
session.flush()#这个为了放回 id
blog = Blog(title=u'第一个', user=user.id)
session.add(blog)
session.commit()

一: 一对多关系

  • 表设计

        class Parent(Base):  # 一__tablename__ = 'parent'id = Column(Integer, primary_key=True)name = Column(String(64),nullable=False)full_name = Column(String(64))children = relationship("Child")# 默认返回的是列表,collection_class=set 加他返回的是集合, #collection_class=attribute_mapped_collection('name') 返回的是name 字典 值从属性中取#collection_class=mapped_collection(lambda children: children.name.lower() 这个可以自定义值# 在父表类中通过 relationship() 方法来引用子表的类集合class Child(Base):  #多__tablename__ = 'child'id = Column(Integer, primary_key=True)name = Column(String(64),nullable=False)full_name = Column(String(64))parent_id = Column(Integer, ForeignKey('parent.id'))# 在子表类中通过 foreign key (外键)引用父表的参考字段
    
  • 添加数据

    parent = Parent(name='morgan', full_name='morganlions')
    parent.children = Child(name='child 1 name',full_name='child 1 full name')
    session.add(parent)
    session.commit()
    #如果parent 已经存在
    parent = session.query(Parent).filter(Parent.name=='Morgan')
    children = [Child(name='child 2', full_name='child 2 full name', parent_obj = parent),Child(name='child 3', full_name='child 3 full name', parent_obj = parent),
    ]
    session.add_all(children)
    session.commit()
    
  • 查询数据(单向关系)

    parent = session.query(Parent).get(1)
    print(parent.children)
    #一对多查询
    parent = session.query(Parent).filter(Parent.children.any(Child.name==u'child 1')).first()
    

二:多对一关系

这个相比上面的一对多而言是双向的关系.

  • 表设计

    class Parent(Base):#一__tablename__ = 'parent'id = Column(Integer, primary_key=True)name = Column(String(64), nullable=False)full_name = Column(String(64))children = relationship("Child", back_populates="parent", lazy="dynamic")class Child(Base):#多__tablename__ = 'child'id = Column(Integer, primary_key=True)name = Column(String(64), nullable=False)full_name = Column(String(64))parent_id = Column(Integer, ForeignKey('parent.id'))parent = relationship("Parent", order_by = 'Parent.id' back_populates="children")# 子表类中附加一个 relationship() 方法# 并且在(父)子表类的 relationship() 方法中使用 relationship.back_populates 参数
    
  • 添加数据

    这个和单向的一样,不过可以反过来操作
    
  • 查询数据(双向关系)

    parent = session.query(Parent).get(1)
    print(parent.children) 
    children = session.query(Child).get(1)
    print(children.parent)
    # lazy="dynamic" 我们添加 这个后可以子查询关联表的时候自由的选择
    print(parent.children.all())
    print(patent.children.filter(Child.name='child1').first()
    #多对一查询父亲
    children = session.query(Child).filter(Child.parent_obj.has(Parent.name=='morgan')).first()
    

三:多对多关系

  1. 单向多对多
    通过中间表 association 关联表 链接 left 和 right 两张表 left -> right ,建立的是单向关系

    # 多对多关系中的两个表之间的一个关联表
    association_table = Table('association', Base.metadata,Column('left_id', Integer, ForeignKey('left.id')),Column('right_id', Integer, ForeignKey('right.id'))
    )class Parent(Base):__tablename__ = 'left'id = Column(Integer, primary_key=True)children = relationship("Child",secondary=association_table)# 在父表中的 relationship() 方法传入 secondary 参数,其值为关联表的表名class Child(Base):__tablename__ = 'right'id = Column(Integer, primary_key=True)
    
  2. 双向多对多
    通过中间表 association 关联表 链接 left 和 right 两张表 left <-> right ,建立的是双向关系

     association_table = Table('association', Base.metadata,Column('left_id', Integer, ForeignKey('left.id')),Column('right_id', Integer, ForeignKey('right.id'))
    )class Parent(Base):__tablename__ = 'left'id = Column(Integer, primary_key=True)children = relationship("Child", secondary=association_table,back_populates="parents")class Child(Base):__tablename__ = 'right'id = Column(Integer, primary_key=True)parents = relationship( "Parent",secondary=association_table,back_populates="children")
    

下面是一种比较简洁的建立双向多对多关系的方法,上面的方法中我们用的是 back_populates ,下面我们将其换成,backref,这样我们只需要在第一个双向关系建立的时候,建立反向双向关系。

association_table = Table('association', Base.metadata,Column('left_id', Integer, ForeignKey('left.id')),Column('right_id', Integer, ForeignKey('right.id'))
)class Parent(Base):__tablename__ = 'left'id = Column(Integer, primary_key=True)name = Column(String(64),nullable=False, index=True)full_name = Column(String(64))children = relationship("Child",secondary=association_table, backref="parents")class Child(Base):__tablename__ = 'right'name = Column(String(64),nullable=False, index=True)full_name = Column(String(64))id = Column(Integer, primary_key=True)

secondary 可以作为回调函数返回一个值

class Parent(Base):__tablename__ = 'left'id = Column(Integer, primary_key=True)children = relationship("Child",secondary=lambda: association_table,backref="parents")

多对多关系添加数据:

parent = Parent(name='Morgan',full_name='MorganLions')
parent.children = [Child(name='child name',full_name='child full name')]
#提交数据
session.add(parent)
session.commit()
#如果是双向关系也可以这样添加
child = Child(name='child name',full_name='child full name'}
child.parents = [Parent(name='Morgan', full_name ='Morgan Lions')]
session.add(child)
session.commit()
session = Session()
#找出数据在添加
parent = session.query(Parent).filter(Parent.name=='Morgan').one()
parent.children =  [Child(name='child name',full_name='child full name')]
session.commit()
#删除多对多中间关系,不删除实体,这里我们对应的是children#1清除parent 中的 children 数据
parent = session.query(Parent).filter(Parent.name='Morgan').one()
parent.children=[]
session.commit()#2删除两个表对应的关系
children = session.query(Child).filter(Child.name='child 1').one()
parent.children.remove(children)
session.commit()
#删除实体,不删除关系
#  1:创建要实验的数据
parent = session.query(Parent).filter(Parent.name=='Morgan').one()
children = Child(name='child 5)
parent.children = [children]
session.commit()
#2: 删除实体
children  = session.query(Child).filter(Child.name=='child 5').one()
session.delete(children)
session.commit()

多对多关系查询数据
1:这是一种方法通过对象的方法去转换,当然你也可以直接返回去用,我需要列表的形式,还有另外的一种方法,自己写个方法在模型文件中,就是你建立数据库文件的表中添加相应的方法,这里我贴出自己的方法,欢迎指正,谢谢。

    #单条数据查询parent = session.query(Parent).get(1)#找到儿子print(parent.children)children = session.query(Child).get(1)#找到父亲print(children.parent)#多条数据查询parent = session.query(Parent).order_by(Parent.id)count = parent.count() #计数page = ... limit = ...parent = parent.offset(page).limit(limit)result = []for p in parent.all():arg ={}arg['id'] = p.idarg['name'] = p.namearg['full_name'] = p.full_namearg['children'] = {c.name:c.full_name for c in p.children}result.append(arg)print(result)

2:在模型中添加如下的方法,这个方法,如果去关联多对多查询,比较慢,因为本质上而言,还是和数据库的多表查询没有什么区别,还是要转换成对应的sql语句去执行,速度自然就慢了,建议在列表中尽量不要用这种多表联合查询。性能很不好。

def column_dict(self, params=None, operation=None, relation=None):'''sql查询返回字典:param params: 需要显示的字段:param operation:操作方法{'one':func1 'two': func2 , 'param':'提示术语'}{'one':func1 'two': 'param':'提示术语'}{'one':func1 'two': func2 }param 作为第二个函数的参数,也可以作为单独的提示:param relation:return:'''model_dict = dict(self.__dict__)del model_dict['_sa_instance_state']if params:keys = [k for k in model_dict.keys()]for key in keys:if key not in params:del model_dict[key]if relation:for r in relation:rel = eval('self.{0}'.format(r))result = [self.change_str(operation, x.column_dict()) for x in rel]model_dict['{0}'.format(r)] = resultif operation:model_dict = self.change_str(operation, model_dict)if isinstance(model_dict, str):return False, 'model_dict', 0return model_dictBase.column_dict = column_dict@staticmethod
def change_str(operation, model_dict):'''改变输出类型:param operation::param model_dict::return:'''for key, funcs in operation.items():method = model_dict[key]func = funcs.get('one')second_func = funcs.get('two')param = funcs.get('param')if param and func and second_func:model_dict[key] = func(method) if method else second_func(param)elif second_func is None and func and param:model_dict[key] = func(method) if method else paramelif param is None and func and second_func:model_dict[key] = func(method) if method else second_func()else:return '操作函数设置错误'return model_dict
#调用
parent = session.query(Parent)operation = {'name: {'one': func, 'param': '参数'},}relation = ['children']result = [x.column_dict(operation=operation, relation=relation) for x in parent]

这个方法也可以不传参数,直接调用,可以直接返回字典,返回的是所有字段,如果要自己选定字段需要传参数params=‘字段1,字段二’,这个方法可以关联很多表,但速度可想而知。在不关联的情况下查询速度180ms 左右,关联一个表600ms 左右,关联 二个 1000ms左右,以此类推。还是那句话,不建议列表中关联查询,更不要用这样的多对多的关系。

sqlalchemy自动关系
看到这里你可能会想,太麻烦了,有没有能自动关联的关系呢,答案是有的,在relationship 中有个参数cascade
cascade 所有的可选字符串项是:

all , 所有操作都会自动处理到关联对象上.
save-update , 关联对象自动添加到会话.
delete , 关联对象自动从会话中删除.
delete-orphan , 属性中去掉关联对象, 则会话中会自动删除关联对象.**只适用于一对多的关系**
merge , session.merge() 时会处理关联对象.
refresh-expire , session.expire() 时会处理关联对象.
expunge , session.expunge() 时会处理关联对象.

delete

class Blog(BaseModel):__tablename__ = 'blog'id = Column(BIGINT, primary_key=True, autoincrement=True)title = Column(String(64), server_default='', nullable=False)user = Column(BIGINT, ForeignKey('user.id'), index=True, nullable=False)class User(BaseModel):__tablename__ = 'user'id = Column(BIGINT, primary_key=True, autoincrement=True)name = Column(String(32), server_default='', nullable=False)blog_list = relationship('Blog', cascade='save-update, delete')if __name__ == '__main__':session = Session()#user = User(name=u'用户')#user.blog_list = [Blog(title=u'哈哈')]#session.add(user)user = session.query(User).first()session.delete(user)session.commit()

merge 有则修改,无则创建

class Blog(BaseModel):__tablename__ = 'blog'id = Column(BIGINT, primary_key=True, autoincrement=True)title = Column(String(64), server_default='', nullable=False)user = Column(BIGINT, ForeignKey('user.id'), index=True, nullable=False)
class User(BaseModel):__tablename__ = 'user'id = Column(BIGINT, primary_key=True, autoincrement=True)name = Column(String(32), server_default='', nullable=False)blog_list = relationship('Blog', cascade='save-update, delete, delete-orphan, merge')
if __name__ == '__main__':session = Session()user = User(id=1, name='1')session.add(user)session.commit(user)user = User(id=1, blog_list=[Blog(title='哈哈')])session.merge(user)session.commit()

属性代理

from sqlalchemy.ext.associationproxy import association_proxyclass Blog(BaseModel):__tablename__ = 'blog'id = Column(Integer, autoincrement=True, primary_key=True)title = Column(Unicode(32), nullable=False, server_default='')user = Column(Integer, ForeignKey('user.id'), index=True)#对应的属性代理设置def __init__(self, title):self.title = titleclass User(BaseModel):__tablename__ = 'user'id = Column(Integer, autoincrement=True, primary_key=True)name = Column(Unicode(32), nullable=False, server_default='')blog_list = relationship('Blog')blog_title_list = association_proxy('blog_list', 'title')

有的时候我们只需要处理对应的一个,比如上面这个,我们指出了blog_list 中的title

#设置与调用
session = Session()user = User(name='xxx')
user.blog_list = [Blog(title='ABC')]
session.add(user)
session.commit()user = session.query(User).first()
print user.blog_title_list
#如果我们希望像下面这样更改数据
user = session.query(User).first()
user.blog_title_list = ['NEW']
session.add(user)
session.commit()
#需要在代理设置那个类中添加对应的 __init__ 方法
def __init__(self, title):self.title = title
#也可以这样 blog_title_list = association_proxy('blog_list', 'title',creator=lambda t: User(title=t))

查询方法

class Blog(BaseModel):__tablename__ = 'blog'id = Column(Integer, autoincrement=True, primary_key=True)title = Column(Unicode(32), server_default='')user = Column(Integer, ForeignKey('user.id'), index=True)user_obj = relationship('User')user_name = association_proxy('user_obj', 'name')

查询:

blog = session.query(Blog).filter(Blog.user_name == u'XX').first()

反过来查询

user = session.query(User).filter(User.blogs_title.contains('A')).first()

这篇关于sqlalchemy 一对多/多对一/多对多/一对一关系定义设置全方位操作方法的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/876287

相关文章

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

POJ1269 判断2条直线的位置关系

题目大意:给两个点能够确定一条直线,题目给出两条直线(由4个点确定),要求判断出这两条直线的关系:平行,同线,相交。如果相交还要求出交点坐标。 解题思路: 先判断两条直线p1p2, q1q2是否共线, 如果不是,再判断 直线 是否平行, 如果还不是, 则两直线相交。  判断共线:  p1p2q1 共线 且 p1p2q2 共线 ,共线用叉乘为 0  来判断,  判断 平行:  p1p

pip-tools:打造可重复、可控的 Python 开发环境,解决依赖关系,让代码更稳定

在 Python 开发中,管理依赖关系是一项繁琐且容易出错的任务。手动更新依赖版本、处理冲突、确保一致性等等,都可能让开发者感到头疼。而 pip-tools 为开发者提供了一套稳定可靠的解决方案。 什么是 pip-tools? pip-tools 是一组命令行工具,旨在简化 Python 依赖关系的管理,确保项目环境的稳定性和可重复性。它主要包含两个核心工具:pip-compile 和 pip

Spring 源码解读:自定义实现Bean定义的注册与解析

引言 在Spring框架中,Bean的注册与解析是整个依赖注入流程的核心步骤。通过Bean定义,Spring容器知道如何创建、配置和管理每个Bean实例。本篇文章将通过实现一个简化版的Bean定义注册与解析机制,帮助你理解Spring框架背后的设计逻辑。我们还将对比Spring中的BeanDefinition和BeanDefinitionRegistry,以全面掌握Bean注册和解析的核心原理。

uniapp设置微信小程序的交互反馈

链接:uni.showToast(OBJECT) | uni-app官网 (dcloud.net.cn) 设置操作成功的弹窗: title是我们弹窗提示的文字 showToast是我们在加载的时候进入就会弹出的提示。 2.设置失败的提示窗口和标签 icon:'error'是设置我们失败的logo 设置的文字上限是7个文字,如果需要设置的提示文字过长就需要设置icon并给

Tomcat性能参数设置

转自:http://blog.csdn.net/chinadeng/article/details/6591542 Tomcat性能参数设置 2010 - 12 - 27 Tomcat性能参数设置 博客分类: Java Linux Tomcat 网络应用 多线程 Socket 默认参数不适合生产环境使用,因此需要修改一些参数   1、修改启动时内存参数、并指定J

浙大数据结构:树的定义与操作

四种遍历 #include<iostream>#include<queue>using namespace std;typedef struct treenode *BinTree;typedef BinTree position;typedef int ElementType;struct treenode{ElementType data;BinTree left;BinTre

类和对象的定义和调用演示(C++)

我习惯把类的定义放在头文件中 Student.h #define _CRT_SECURE_NO_WARNINGS#include <string>using namespace std;class student{public:char m_name[25];int m_age;int m_score;char* get_name(){return m_name;}int set_name

c++ 定义二位数组

在 C++ 中,定义二维数组有几种常见的方式。以下是几个示例: 1. 静态二维数组 定义: int array[3][4]; 这里,array 是一个 3 行 4 列的整数二维数组。 初始化: int array[3][4] = {{1, 2, 3, 4},{5, 6, 7, 8},{9, 10, 11, 12}}; 2. 动态二维数组 使用指针和动态内存分配: 定义:

linux下非标准波特率的设置和使用

通常,在linux下面,设置串口使用终端IO的相关函数设置,如tcsetattr等函数,linux内部有一个对常用波特率列表的索引,根据设置的波特率用底层驱动来设置异步通信芯片的寄存器 对于非标准的任意波特率需要用ioctl(fd, TIOCGSERIAL, p)和ioctl(fd, TIOCSSERIAL, p)的配合,ioctl的最后一个参数是struct serial_struct *