SQLAlchemy(三):外键、连表关系
SQLAlchemy03 /外键、连表关系
1、外键
外键简述
使用SQLAlchemy创建外键非常简单。在从表中增加一个字段,指定这个字段外键的是哪个表的哪个字段就可以了。从表中外键的字段,必须和父表的主键字段类型保持一致。
示例代码如下:
- class User(Base):
- __tablename__ = 'user'
- id = Column(Integer,primary_key=True,autoincrement=True)
- username = Column(String(50),nullable=False)
- class Article(Base):
- __tablename__ = 'article'
- id = Column(Integer,primary_key=True,autoincrement=True)
- title = Column(String(50),nullable=False)
- content = Column(Text,nullable=False)
- uid = Column(Integer,ForeignKey("user.id"))
- class User(Base):
外键约束有以下几项:
1. RESTRICT:父表数据被删除,会阻止删除。默认就是这一项。
2. NO ACTION:在MySQL中,同RESTRICT。
3. CASCADE:级联删除。
4. SET NULL:父表数据被删除,子表数据会设置为NULL。
2、ORM关系以及一对多
简述
mysql级别的外键,还不够ORM,必须拿到一个表的外键,然后通过这个外键再去另外一张表中查找,这样太麻烦了。SQLAlchemy提供了一个
relationship
,这个类可以定义属性,以后在访问相关联的表的时候就直接可以通过属性访问的方式就可以访问得到了。示例代码:
- class User(Base):
- __tablename__ = 'user'
- id = Column(Integer,primary_key=True,autoincrement=True)
- username = Column(String(50),nullable=False)
- # articles = relationship("Article")
- def __repr__(self):
- return "<User(username:%s)>" % self.username
- class Article(Base):
- __tablename__ = 'article'
- id = Column(Integer,primary_key=True,autoincrement=True)
- title = Column(String(50),nullable=False)
- content = Column(Text,nullable=False)
- uid = Column(Integer,ForeignKey("user.id"))
- author = relationship("User",backref="articles")
- # 创建一对多数据
- user = User(username='zhangsan')
- session.add(user)
- session.commit()
- article = Article(title='abc',content='',uid=1)
- session.add(article)
- session.commit()
- # 查询一对多数据
- article = session.query(Article).first()
- uid = article.uid
- print(article)
- user = session.query(User).get(uid)
- print(user)
- class User(Base):
另外,可以通过
backref
来指定反向访问的属性名称。articles是有多个。他们之间的关系是一个一对多的关系
3、一对一的关系
简述
在sqlalchemy中,如果想要将两个模型映射成一对一的关系,那么应该在父模型中,指定引用的时候,要传递一个
uselist=False
这个参数进去。就是告诉父模型,以后引用这个从模型的时候,不再是一个列表了,而是一个对象了。示例代码如下:
- class User(Base):
- __tablename__ = 'user'
- id = Column(Integer,primary_key=True,autoincrement=True)
- username = Column(String(50),nullable=False)
- extend = relationship("UserExtend",uselist=False)
- def __repr__(self):
- return "<User(username:%s)>" % self.username
- class UserExtend(Base):
- __tablename__ = 'user_extend'
- id = Column(Integer, primary_key=True, autoincrement=True)
- school = Column(String(50))
- uid = Column(Integer,ForeignKey("user.id"))
- user = relationship("User",backref="extend")
简化代码:可以借助
sqlalchemy.orm.backref
来简化代码- from sqlalchemy.orm import sessionmaker,relationship,backref
- class User(Base):
- __tablename__ = 'user'
- id = Column(Integer,primary_key=True,autoincrement=True)
- username = Column(String(50),nullable=False)
- # 方式一:
- # extend = relationship("UserExtend",uselist=False)
- def __repr__(self):
- return "<User(username:%s)>" % self.username
- class UserExtend(Base):
- __tablename__ = 'user_extend'
- id = Column(Integer, primary_key=True, autoincrement=True)
- school = Column(String(50))
- uid = Column(Integer,ForeignKey("user.id"))
- # 方式二:
- user = relationship("User",backref=backref("extend",uselist=False))
- # 创建一对一数据
- user = User(username='zhangsan')
- extend1 = UserExtend(school='qinghua daxue')
- user.extend = extend1
- session.add(user)
- session.commit()
- # 查看一对一数据
- # 可以直接根据属性去查询对应的数据
- class User(Base):
4、多对多的关系
简述
- 多对多的关系需要通过一张中间表来绑定他们之间的关系。
- 先把两个需要做多对多的模型定义出来
- 使用Table定义一个中间表,中间表一般就是包含两个模型的外键字段就可以了,并且让他们两个来作为一个“复合主键”。
- 在两个需要做多对多的模型中随便选择一个模型,定义一个relationship属性,来绑定三者之间的关系,在使用relationship的时候,需要传入一个secondary=中间表。
代码示例:
- article_tag = Table(
- "article_tag",
- Base.metadata,
- Column("article_id",Integer,ForeignKey("article.id"),primary_key=True),
- Column("tag_id",Integer,ForeignKey("tag.id"),primary_key=True)
- )
- class Article(Base):
- __tablename__ = 'article'
- id = Column(Integer,primary_key=True,autoincrement=True)
- title = Column(String(50),nullable=False)
- # 方式一:
- # tags = relationship("Tag",backref="articles",secondary=article_tag)
- def __repr__(self):
- return "<Article(title:%s)>" % self.title
- class Tag(Base):
- __tablename__ = 'tag'
- id = Column(Integer, primary_key=True, autoincrement=True)
- name = Column(String(50), nullable=False)
- # 方式二:
- articles = relationship("Article",backref="tags",secondary=article_tag)
- def __repr__(self):
- return "<Tag(name:%s)>" % self.name
- # 添加多对多数据
- article1 = Article(title="article1")
- article2 = Article(title="article2")
- tag1 = Tag(name='tag1')
- tag2 = Tag(name='tag2')
- article1.tags.append(tag1)
- article1.tags.append(tag2)
- article2.tags.append(tag1)
- article2.tags.append(tag2)
- session.add(article1)
- session.add(article2)
- session.commit()
- # 查询多对多数据
- article = session.query(Article).first()
- print(article.tags)
- tag = session.query(Tag).first()
- print(tag.articles)
- article_tag = Table(
5、ORM层面的删除数据
简述:
ORM层面删除数据,会无视mysql级别的外键约束。直接会将对应的数据删除,然后将从表中的那个外键设置为NULL。如果想要避免这种行为,应该将从表中的外键的
nullable=False
。代码示例:
- class User(Base):
- __tablename__ = 'user'
- id = Column(Integer,primary_key=True,autoincrement=True)
- username = Column(String(50),nullable=False)
- class Article(Base):
- __tablename__ = 'article'
- id = Column(Integer, primary_key=True, autoincrement=True)
- title = Column(String(50),nullable=False)
- uid = Column(Integer,ForeignKey("user.id"),nullable=False)
- author = relationship("User",backref='articles')
- # Base.metadata.drop_all()
- # Base.metadata.create_all()
- #
- # user = User(username='zhiliao')
- # article = Article(title='hello world')
- # article.author = user
- # session.add(article)
- # session.commit()
- # orm层面的删除,删除User时Article表关联的字段会置为NULL
- # 将uid设置为nullable=False,就不会造成上面的orm层面的删除
- user = session.query(User).first()
- session.delete(user)
- session.commit()
- class User(Base):
6、ORM层面的CASCADE
在SQLAlchemy,只要将一个数据添加到session中,和他相关联的数据都可以一起存入到数据库中了。这些是怎么设置的呢?其实是通过relationship的时候,有一个关键字参数cascade可以设置这些属性:
- save-update:默认选项。在添加一条数据的时候,会把其他和他相关联的数据都添加到数据库中。这种行为就是save-update属性影响的。
- delete:表示当删除某一个模型中的数据的时候,是否也删掉使用relationship和他关联的数据。
- delete-orphan:表示当对一个ORM对象解除了父表中的关联对象的时候,自己便会被删除掉。当然如果父表中的数据被删除,自己也会被删除。这个选项只能用在一对多上,不能用在多对多以及多对一上。并且还需要在子模型中的relationship中,增加一个single_parent=True的参数。
- merge:默认选项。当在使用session.merge,合并一个对象的时候,会将使用了relationship相关联的对象也进行merge操作。
- expunge:移除操作的时候,会将相关联的对象也进行移除。这个操作只是从session中移除,并不会真正的从数据库中删除。
- all:是对save-update, merge, refresh-expire, expunge, delete几种的缩写。
代码示例:
示例一:正常添加数据,默认cascade="save-update"
- class User(Base):
- __tablename__ = 'user'
- id = Column(Integer,primary_key=True,autoincrement=True)
- username = Column(String(50),nullable=False)
- class Article(Base):
- __tablename__ = 'article'
- id = Column(Integer, primary_key=True, autoincrement=True)
- title = Column(String(50), nullable=False)
- uid = Column(Integer,ForeignKey("user.id"),nullable=False)
- author = relationship("User",backref="articles")
- Base.metadata.drop_all()
- Base.metadata.create_all()
- user = User(username='zhangsan')
- article = Article(title='title')
- article.author = user
- session.add(user)
- session.commit()
示例二:cascade=""
- class User(Base):
- __tablename__ = 'user'
- id = Column(Integer,primary_key=True,autoincrement=True)
- username = Column(String(50),nullable=False)
- class Article(Base):
- __tablename__ = 'article'
- id = Column(Integer, primary_key=True, autoincrement=True)
- title = Column(String(50), nullable=False)
- uid = Column(Integer,ForeignKey("user.id"),nullable=False)
- author = relationship("User",backref="articles",cascade="")
- Base.metadata.drop_all()
- Base.metadata.create_all()
- user = User(username='zhangsan')
- article = Article(title='title')
- article.author = user
- session.add(user) # 这样只会添加user不会添加article
- session.commit()
示例三:cascade="delete"
- class User(Base):
- __tablename__ = 'user'
- id = Column(Integer,primary_key=True,autoincrement=True)
- username = Column(String(50),nullable=False)
- class Article(Base):
- __tablename__ = 'article'
- id = Column(Integer, primary_key=True, autoincrement=True)
- title = Column(String(50), nullable=False)
- uid = Column(Integer,ForeignKey("user.id"),nullable=False)
- author = relationship("User",backref="articles",cascade="save-update,delete")
- # 删除该文章,会将该文章对应的User删除
- def my_init_db():
- """ 用来初始化数据 """
- Base.metadata.drop_all()
- Base.metadata.create_all()
- user = User(username='zhangsan')
- article = Article(title='title')
- article.author = user
- comment = Comment(content='xxx')
- comment.author = user
- session.add(comment)
- session.add(article)
- session.commit()
- def operation():
- article = session.query(Article).first()
- session.delete(article)
- session.commit()
- if __name__ == '__main__':
- # my_init_db()
- operation()
示例四:cascade="delete-orphan"删除孤儿数据,结合single_parent=True使用
- class User(Base):
- __tablename__ = 'user'
- id = Column(Integer,primary_key=True,autoincrement=True)
- username = Column(String(50),nullable=False)
- class Article(Base):
- __tablename__ = 'article'
- id = Column(Integer, primary_key=True, autoincrement=True)
- title = Column(String(50), nullable=False)
- uid = Column(Integer,ForeignKey("user.id"),nullable=False)
- author = relationship("User",backref=backref("articles",cascade="save-update,delete,delete-orphan"),cascade="save-update",single_parent=True)
- def my_init_db():
- Base.metadata.drop_all()
- Base.metadata.create_all()
- user = User(username='zhangsan')
- article = Article(title='title')
- article.author = user
- session.add(article)
- session.commit()
- def operation():
- user = session.query(User).first()
- user.articles = []
- session.commit()
- session.commit()
- if __name__ == '__main__':
- # my_init_db()
- operation()
- class User(Base):
SQLAlchemy(三):外键、连表关系的更多相关文章
- Django(15)外键和表关系
外键删除操作 如果一个模型使用了外键.那么在对方那个模型被删掉后,该进行什么样的操作.可以通过on_delete来指定.可以指定的类型如下: CASCADE:级联操作.如果外键对应的那条数据被删除了, ...
- mysql外键与表查询
目录 自增特性 外键 外键关系 外键创建 外键的约束效果 级联更新级联删除 多对多关系 一对一关系 表查询关键字 select与from where筛选 group by分组 练习 关系练习 查询练习 ...
- oracle查询某张表的外键,并用 truncate 命令有外键的表中的数据
注:本文来源于<oracle查询某张表的外键(最终解决办法)> 一:几个查询表外键的脚本 select b.table_name, b.column_name from user_cons ...
- 主外键多表查询demo
https://www.cnblogs.com/DragonFire/p/6949767.html mySQL练习-主外键多表查询 MySQL练习-主外键多表查询 练习: 1.建立表关系: 请创建如下 ...
- Flask-SQLAlchemy - 不使用外键连表查询。记得常回来看我
前言 相比于 Django 的 ORM ,SQLAlchemy "不依靠外键进行跨表联查" 的解决方案就比较多. 没啥好说的,只能怪自己学艺不精.. _(:з」∠)_ 解决办法 ...
- 删除带外键的表【foreign key constraint fails】报错
title: 删除带外键的表[foreign key constraint fails]报错 date: 2018-08-02 21:59:06 tags: 数据库 --- 遥想当时正在学hibern ...
- 【Hibernate】无外键多表查询
无外键多表查询时编写hql,直接使用逗号分隔表,where作为联合查询条件进行查询.查询出来的结果可为两种,List<List<Object>>或者List<Map< ...
- MySQ-表关系-外键-修改表结构-复制表-03
目录 前言 不合理的表结构(案例) 带来的问题 如何解决问题? 如何确定表关系? 表关系 一对多 多对多 一对一 应用场景 判断表关系最简单的语法 三种关系常见案例 如何建立表关系? 外键 forei ...
- Django中ORM外键和表的关系(Django编程-4)
外键 在MySQL中,表有两种引擎,一种是InnoDB,另外一种是myisam.如果使用的是InnoDB引擎,是支持外键约束的.外键的存在使得ORM框架在处理表关系的时候异常的强大.因此这里我们首先来 ...
- Python Django orm操作数据库笔记之外键和表关系
外键 在MySQL中,表有两种引擎,一种是InnoDB,另外一种是myisam.如果使用的是InnoDB引擎,是支持外键约束的. 外键的使用 使用外键前需要先确保相应外键已存储在数据库中(flask中 ...
随机推荐
- vs2017离线包下载获取方法
一.去官网下载所需要的版本的安装包获取程序: https://www.visualstudio.com/zh-hans/downloads/ 三个版本,对应文件名称为: 社区版:vs_Communit ...
- 【K8S学习笔记】初识K8S 及架构组件
K8S是什么?发展历史 Kubernetes (简称 k8s)是 Google 在2014年开源的,对容器生命周期管理的开源平台,致力于对容器集群提供易于管理.高可用.弹性负载与故障转移的能力,提高服 ...
- SpringBoot读取application.properties中文乱码
[本文版权归微信公众号"代码艺术"(ID:onblog)所有,若是转载请务必保留本段原创声明,违者必究.若是文章有不足之处,欢迎关注微信公众号私信与我进行交流!] 解决方案 在ID ...
- IntelliJ IDEA 2018.3.6 安装、激活 JRebel
在 IntelliJ IDEA 2018.3.6 中安装## JRebel 1.代开 IDEA 开发工具,然后用快捷键 Ctrl+Alt+S 打开设置并搜索 jrebel 插件 2.安装 jrebel ...
- 2020/6/11 JavaScript高级程序设计 DOM
DOM(文档对象模型)是针对HTML和XML文档的一个API(应用程序接口).他描绘了一个层次化的节点树,允许开发人员添加.移除和修改页面的某一部分. 10.1 节点层次 DOM将任何HTML和XML ...
- dart快速入门教程 (2)
2.变量和数据类型 2.1.变量和常量 变量通俗的说就是可以变化的量,作用就是用来存储数据,你可以把一个变量看作是一个水果篮,里面可以装苹果.梨.香蕉等,常量就是一个固定的值,和变量是相对的,变量可以 ...
- 46道Linux面试题送给你(后续会不断更新)
绝对路径用什么符号表示? 当前目录.上层目录用什么表示?主目录用什么表示? 切换目录用什么命令? 答案: # 绝对路径: 如/etc/init.d # 当前目录和上层目录: ./ ../ # 主目录: ...
- 【贪心】Emergency Evacuation
题目 大致题意 把指定的人从同一出口送出车外,且同一位置不能同时有两个人,求所需的最短时间. 分析 第一感觉就是利用贪心思想解决问题,但是这道题的数据范围用模拟的话肯定是会爆掉的,所以这是不可取的.我 ...
- 【Spring】原来SpringBoot是这样玩的
菜瓜:我自己去调Mvc的源码差点没给Spring的逻辑秀死...难受 水稻:那今天咱们看一个简单易用的SpringBoot吧 菜瓜:可以,这个我熟悉 水稻:熟悉? 菜瓜:当我没说,请开始你的表演 水稻 ...
- 【Flutter 实战】简约而不简单的计算器
老孟导读:这是 [Flutter 实战]组件系列文章的最后一篇,其他组件地址:http://laomengit.com/guide/widgets/Text.html,接下来将会讲解动画系列,关注老孟 ...