SQLAlchemy 多态进阶(__mapper_args__ )、多对多标签
什么是polymorphic
熟悉SQLAlchemy的人往往知道polymorphic(多态)的模型定义,如果你恰好不熟悉SQLAlchemy,这里简单的举一个例子:
class Employee(Base): __tablename__ = 'employee'id = Column(Integer, primary_key=True)name = Column(String(50))type = Column(String(50))__mapper_args__ = {'polymorphic_identity':'employee','polymorphic_on':type}
这里定义了雇员Employee 模型,指定type字段为多态所在字段,并且对于这个模型,当type字段为'employee'时,即为一个雇员
再看下面这两个继承之后的模型
class Engineer(Employee): __tablename__ = 'engineer'id = Column(Integer, ForeignKey('employee.id'), primary_key=True)engineer_name = Column(String(30))__mapper_args__ = {'polymorphic_identity':'engineer',}class Manager(Employee): __tablename__ = 'manager'id = Column(Integer, ForeignKey('employee.id'), primary_key=True)manager_name = Column(String(30))__mapper_args__ = {'polymorphic_identity':'manager',}
这里又定义了两个模型,Engineer,Manager,
并对应了两张表,这两张表的结构除了有少许不同,类似的,polymorphic_identity指定了这两种模型对应的type字段值,
在上面的基础上,可以提出的问题:
- 可不可以完全在一张表上实现这样的多态?
- 这样的模型可以用作M2M关系吗?
两者的答案显然是肯定的。 对于第一个问题,只需要使得后两者的__tablename__ = None,并且不指定额外的字段即可。
第二个问题,即这几天我的重构的探索
如何设置多对多模型
对于一个多对多的关系表,按照 SQLAlchemy 文档:
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)class Child(Base): __tablename__ = 'right'id = Column(Integer, primary_key=True)
(虽然我们的基本不会按照SQLAlchemy那样定义ForeignKey了,万恶的ForeignKey。。)
关键在于应当有第三张表,存放M2M的关系。上面的association,就是这样的一张M2M表,有两个字段left_id和right_id
而且显然的,我们可以轻松地想象出取出M2M关系的SQL:
select left.id,right.id from left join association on left.id=association.left_id
join right on association.right_id=right.id
是借助association实现两个表的JOIN关系
SQLAlchemy 的对应操作这里就不赘述了,大家看文档吧咩哈哈
M2M和多态
此次重构遇到的问题就是:如果我们的M2M的关系,如果是在多态上进行的,例如上面的Child,如果我不仅仅有Child,还分Boy和Girl,如何在这一张association_table进行控制呢? 上面代码先稍作修改:
class Association(Base): left_id = Column('left_id', Integer, ForeignKey('left.id')),right_id = Column('right_id', Integer, ForeignKey('right.id'))gender = Column('gender', Boolean)__mapper_args__ = {"polymorphic_on": gender}
增加了gender字段,并且增加了多态声明__mapper_args__ 我们先假设一下这样的SQL该怎么写吧,实际上是很简单的哈:
select left.id,right.id from left join association on (left.id=association.left_id and association.gender)
join right on association.right_id=right.id
join的时候额外加一个字段即可。
如何让SQLalchemy可以生成出这样的SQL,并且还自动进行例如增删查改的SQL声明呢?
SQLAlchemy同样给出了对应的 样例
我基于这个样例做了一定的修改:
- 完全不用ForeignKey声明。这一点很容易,from sqlalchemy.orm import foreign,用foreign函数包一下对应的字段,就可以当成外键来用
- 样例中的Address声明了association = relationship("AddressAssociation", backref="addresses"),这样使得AddressAssociation有了一个addresses的反向引用(backref),在实际的M2M模型设计中,考虑到是跨模块的模型映射,为了方便修改和维护,没有修改M2M左边的这个M,因此在AddressAssociation中动态声明了一个addresses
- 增加relationship cascade属性,以进行删除操作
对应的diff如下(稍微修改了字段名),稍后有完整代码:
--- origin.py 2016-10-13 11:28:57.000000000 +0800
+++ target.py 2016-10-13 11:29:44.000000000 +0800
@@ -1,80 +1,84 @@ from sqlalchemy.ext.declarative import as_declarative, declared_attrfrom sqlalchemy import create_engine, Integer, Column, \
- String, ForeignKey
-from sqlalchemy.orm import Session, relationship, backref
+ String, and_
+from sqlalchemy.orm import Session, foreign, relationship, backref from sqlalchemy.ext.associationproxy import association_proxyclass AddressAssociation(Base):"""Associates a collection of Address objectswith a particular parent."""__tablename__ = "address_association"
-
- discriminator = Column(String)
+ addr_id = Column(Integer,
+ primary_key=True,
+ )
+ order_id = Column(Integer,
+ primary_key=True,
+ )
+ discriminator = Column(String, primary_key=True) """Refers to the type of parent."""__mapper_args__ = {"polymorphic_on": discriminator}class Address(Base):"""The Address class.This represents all address records in asingle table."""
- association_id = Column(Integer, ForeignKey("address_association.id"))
+ id = Column(Integer, primary_key=True) street = Column(String)city = Column(String)zip = Column(String)
- association = relationship("AddressAssociation", backref="addresses")
-
- parent = association_proxy("association", "parent")def __repr__(self):return "%s(street=%r, city=%r, zip=%r)" % \(self.__class__.__name__, self.street,self.city, self.zip)class HasAddresses(object):"""HasAddresses mixin, creates a relationship tothe address_association table for each parent."""@declared_attr
- def address_association_id(cls):
- return Column(Integer, ForeignKey("address_association.id"))
-
- @declared_attr def address_association(cls):name = cls.__name__discriminator = name.lower()assoc_cls = type("%sAddressAssociation" % name,(AddressAssociation, ),dict(__tablename__=None,__mapper_args__={"polymorphic_identity": discriminator
- }
- )
+ },
+ addresses=relationship(
+ Address,
+ primaryjoin="Address.idforeign({assoc_cls_name}.addr_id)".format(assoc_cls_name=assoc_cls_name))
+ ) )cls.addresses = association_proxy("address_association", "addresses",creator=lambda addresses: assoc_cls(addresses=addresses))return relationship(assoc_cls,
- backref=backref("parent", uselist=False))
+ primaryjoin=and_(foreign(assoc_cls.addr_id) Address.id,
+ foreign(assoc_cls.order_id) == cls.id),
+ cascade="save-update, merge, delete, delete-orphan",
+ )class Customer(HasAddresses, Base):name = Column(String)class Supplier(HasAddresses, Base):company_name = Column(String)
此后就可以通过Customer.addresses.append等操作M2M了
SQLAlchemy 多态进阶(__mapper_args__ )、多对多标签相关推荐
- sqlalchemy表关系之多对多
sqlalchemy表之间的关系有三种:1.多对多 2.一对多 3.一对一 下面就讲讲sqlalchemy表之间如何建立多对多关系. 首先,我们把两个需要做多对多关系的模型定义出来,这里以Arct ...
- SQLAlchemy 教程 —— 进阶篇
使用 首先是连接到数据库,SQLALchemy支持多个数据库引擎,不同的数据库引擎连接字符串不一样,常用的有 mysql://username:password@hostname/database p ...
- 【Shader进阶】SubShader块标签Tags——IgnoreProjector
目录 一.创建空物体,挂载Projector,并赋值Material(作用:将Projector范围内的物体材质替换为Projector的Material) 二.我们的主角(带有忽略Projector ...
- 第1关:封装、继承和多态进阶(一)
测试说明 测试输入: 泰迪 male brown 波斯猫 male 2.5 预期输出: 名称:泰迪,性别:male,颜色:brown,汪汪叫 泰迪吃骨头! 名称:波斯猫,性别:male,体重:2.5k ...
- 【Hibernate步步为营】--关联映射之多对一
上篇文章讨论了Hibernate的基本映射,一个实体类对应着一张表,在相应的Hibernate Mapping文件中使用<class>标签映射.并且实体类中的普通属性对应着表字段,使用&l ...
- python之SQLAlchemy ORM
前言: 这篇博客主要介绍下SQLAlchemy及基本操作,写完后有空做个堡垒机小项目.有兴趣可看下python之数据库(mysql)操作.下篇博客整理写篇关于Web框架和django基础~~ 一.OR ...
- Mybatis的where标签,还有这么多知识点
背景 在上篇文章,我们系统地学习了where 1=1 相关的知识点,大家可以回看<不要再用where 1=1了!有更好的写法!>这篇文章.文章中涉及到了Mybatis的替代方案,有好学的朋 ...
- 【Android春招每日一练】(十六) 剑指4题+Android进阶
文章目录 概览 剑指offer 1.61 翻转单词顺序 1.62 左旋转字符串 1.63 滑动窗口的最大值 1.64 队列的最大值 Android进阶 Android布局优化 Android权限处理 ...
- 头歌实践实践教学平台:Java面向对象 - 封装、继承和多态的综合练习
第1关:封装.继承和多态进阶(一) 任务描述 本关任务:按要求编写一个Java应用程序,巩固Java面向对象知识. 相关知识 为了完成本关任务,我们回顾一下前面所学知识:1.面向对象思想 :2.封装: ...
最新文章
- Loadrunner 性能测试服务器监控指标
- @RequestBody, @ResponseBody 注解详解
- 快速入门 Nginx,这篇就够了!
- vim trick之 vimrc更改立即生效
- 直播 | 天津大学副教授张长青:多模态融合的基础问题及算法研究
- [react] 在React中你有遇到过安全问题吗?怎么解决?
- 改变JavaScript代码行的背景色
- 《梦幻西游》打响反盗号战役:为2亿玩家提供360安全武器
- 生成可编辑的pdf(可java代码动态赋值)
- Python实战之12306抢票
- VMWare IOS MAC分区教程
- 字符串与16进制之间的转换
- 如何构建基于数字孪生的智慧全息路口
- OpenJudge Tian Ji -- The Horse Racing
- 数字图像处理学习笔记(十五)——图像复原与重建
- 欧拉公式-python程序-计算机仿真方法(入门级)
- Git使用-git init
- 在英特尔硬件上部署深度学习模型的无代码方法 OpenVINO 深度学习工作台的三部分系列文章 - CPU AI 第一部
- MTK5G模块芯片MTK6873_MT6873数据手册/datasheet/规格书
- 【知识学习】简易OI/ACM竞赛测试环境lemon使用方法
热门文章
- M1兼容性怎么样?关于M1版MacBook兼容软件的测试方法
- 提高篇 第三部分 图论 第1章 最小生成树
- 1.6 编程基础之一维数组 11 大整数减法
- 1.4编程基础之逻辑表达式与条件分支 05 整数大小比较
- 华为 HarmonyOS2.0(鸿蒙OS) 开发者beta公测招募的报名流程
- php通过使用curl获取http或者https的响应信息的方式
- 【ES9(2018)】String 扩展 标签模板里字符串转义
- dedecms二次开发常用代码
- PHP笔记-连接MySQL数据库及查询数据
- C++笔记-二维棋盘数组使用BFS(宽度优先遍历)