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"))
  • 外键约束有以下几项:

    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)
  • 另外,可以通过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() # 查看一对一数据
    # 可以直接根据属性去查询对应的数据

4、多对多的关系

  • 简述

    1. 多对多的关系需要通过一张中间表来绑定他们之间的关系。
    2. 先把两个需要做多对多的模型定义出来
    3. 使用Table定义一个中间表,中间表一般就是包含两个模型的外键字段就可以了,并且让他们两个来作为一个“复合主键”。
    4. 在两个需要做多对多的模型中随便选择一个模型,定义一个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)

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()

6、ORM层面的CASCADE

  • 在SQLAlchemy,只要将一个数据添加到session中,和他相关联的数据都可以一起存入到数据库中了。这些是怎么设置的呢?其实是通过relationship的时候,有一个关键字参数cascade可以设置这些属性:

    1. save-update:默认选项。在添加一条数据的时候,会把其他和他相关联的数据都添加到数据库中。这种行为就是save-update属性影响的。
    2. delete:表示当删除某一个模型中的数据的时候,是否也删掉使用relationship和他关联的数据。
    3. delete-orphan:表示当对一个ORM对象解除了父表中的关联对象的时候,自己便会被删除掉。当然如果父表中的数据被删除,自己也会被删除。这个选项只能用在一对多上,不能用在多对多以及多对一上。并且还需要在子模型中的relationship中,增加一个single_parent=True的参数。
    4. merge:默认选项。当在使用session.merge,合并一个对象的时候,会将使用了relationship相关联的对象也进行merge操作。
    5. expunge:移除操作的时候,会将相关联的对象也进行移除。这个操作只是从session中移除,并不会真正的从数据库中删除。
    6. 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()

SQLAlchemy(三):外键、连表关系的更多相关文章

  1. Django(15)外键和表关系

    外键删除操作 如果一个模型使用了外键.那么在对方那个模型被删掉后,该进行什么样的操作.可以通过on_delete来指定.可以指定的类型如下: CASCADE:级联操作.如果外键对应的那条数据被删除了, ...

  2. mysql外键与表查询

    目录 自增特性 外键 外键关系 外键创建 外键的约束效果 级联更新级联删除 多对多关系 一对一关系 表查询关键字 select与from where筛选 group by分组 练习 关系练习 查询练习 ...

  3. oracle查询某张表的外键,并用 truncate 命令有外键的表中的数据

    注:本文来源于<oracle查询某张表的外键(最终解决办法)> 一:几个查询表外键的脚本 select b.table_name, b.column_name from user_cons ...

  4. 主外键多表查询demo

    https://www.cnblogs.com/DragonFire/p/6949767.html mySQL练习-主外键多表查询 MySQL练习-主外键多表查询 练习: 1.建立表关系: 请创建如下 ...

  5. Flask-SQLAlchemy - 不使用外键连表查询。记得常回来看我

    前言 相比于 Django 的 ORM ,SQLAlchemy "不依靠外键进行跨表联查" 的解决方案就比较多. 没啥好说的,只能怪自己学艺不精..  _(:з」∠)_ 解决办法 ...

  6. 删除带外键的表【foreign key constraint fails】报错

    title: 删除带外键的表[foreign key constraint fails]报错 date: 2018-08-02 21:59:06 tags: 数据库 --- 遥想当时正在学hibern ...

  7. 【Hibernate】无外键多表查询

    无外键多表查询时编写hql,直接使用逗号分隔表,where作为联合查询条件进行查询.查询出来的结果可为两种,List<List<Object>>或者List<Map< ...

  8. MySQ-表关系-外键-修改表结构-复制表-03

    目录 前言 不合理的表结构(案例) 带来的问题 如何解决问题? 如何确定表关系? 表关系 一对多 多对多 一对一 应用场景 判断表关系最简单的语法 三种关系常见案例 如何建立表关系? 外键 forei ...

  9. Django中ORM外键和表的关系(Django编程-4)

    外键 在MySQL中,表有两种引擎,一种是InnoDB,另外一种是myisam.如果使用的是InnoDB引擎,是支持外键约束的.外键的存在使得ORM框架在处理表关系的时候异常的强大.因此这里我们首先来 ...

  10. Python Django orm操作数据库笔记之外键和表关系

    外键 在MySQL中,表有两种引擎,一种是InnoDB,另外一种是myisam.如果使用的是InnoDB引擎,是支持外键约束的. 外键的使用 使用外键前需要先确保相应外键已存储在数据库中(flask中 ...

随机推荐

  1. 青蛙的约会 (ax+by=c求最小整数解)【拓展欧几里得】

                                                  青蛙的约会(点击跳转) 两只青蛙在网上相识了,它们聊得很开心,于是觉得很有必要见一面.它们很高兴地发现它们住 ...

  2. Java——几点重要知识笔记(一)

    学了Java有一段时间了,自认为有一些基础知识比较重要,因此记下来共享,不喜勿喷. 一.标识符 (1)定义:在Java语言中,凡是对类,方法,变量,包,参数等命名时,所使用的字符序列 (2)包含的内容 ...

  3. MyISAM和InnoDB的差异

    转载:https://www.cnblogs.com/xc-chejj/p/11245034.html MyISAM是默认表类型,基于ISAM(Indexed Sequential Access Me ...

  4. MFC的Spin Control基础控件的使用

    1.向GUI界面添加一个MFC 提供的Spin数值调节控件 2.设置其"伙伴“,并设置数值调节的范围 3.如何让数值显示在文本框中?你可以有多种选择:可以让文本框控件绑定一个数值类型的变量: ...

  5. 【案例演示】JVM之强引用、软引用、弱引用、虚引用

    1.背景 想要理解对象什么时候回收,就要理解到对象引用这个概念,于是有了下文 2.java中引用对象结构图 3.引用详解 3.1.什么是强引用 a.当内存不足,JVM开始垃圾回收,对于强引用的对象,就 ...

  6. ViewPager2 学习

    ViewPager2 延迟加载数据 ViewPager2 延迟加载数据 ViewPager 实现预加载的方案 ViewPager2 实现预加载的方案 总结 ViewPager 实现预加载的方案 背景 ...

  7. ArrayList、Vector、LinkedList 区别及底层实现

    一.ArrayList.Vector.LinkedList 三者区别 1.ArrayList  底层:是数组结构,查询快,增删慢,线程不安全,效率高.2.LinkedList底层:是链表数据结构,查询 ...

  8. redis高级命令1

    设置name的过期时间是20秒 redis默认是16个数据库,默认是将数据存储在第0个数据库中 因为默认是0,当你选择其他数据的时候,是没有值的

  9. 前端笔记(关于解决打包时报node-sass错误的问题)

    这个问题之前反复出现,试过重新从其他同事将node_modules拿过来用,但是过了几天又出同样的问题 去网上百度了好久,大多数都说是node-sass重装一下就行.可是我这边卸载都无法卸载,何谈重装 ...

  10. PHP字符串函数总结

    字符串函数 addcslashes — 为字符串里面的部分字符添加反斜线转义字符 addslashes — 用指定的方式对字符串里面的字符进行转义 bin2hex — 将二进制数据转换成十六进制表示 ...