【SQLAlchemy】03-SQLAlchemy关联关系

好风凭借力,送我上青云。

使用SQLAlchemy创建外键非常简单,在子表增加一个字段,类型与关联的父表字段类型一致

外键

class Parent(Base):
    __tablename__ = 'parent'
    id = Column(Integer, primary_key=True)
    children = relationship("Child")

class Child(Base):
    __tablename__ = 'child'
    id = Column(Integer, primary_key=True)
    parent_id = Column(Integer, ForeignKey('parent.id'))

上面的代码ParentChild建立了外键关系,子表中的外键parent_id关联着父表的主键id,所以当前字段类型需要与父表的id类型一致,其次ForeignKey('parent.id')中的参数是表名.字段名,且请勿写成模型名.字段名

 parent_id = Column(Integer, ForeignKey('parent.id'))

外键约束:

  • RESTRICT:父表数据被删除,会阻止删除。默认就是这一项。
  • NO ACTION:在MySQL中,同RESTRICT。
  • CASCADE:级联删除。
  • SET NULL:父表数据被删除,子表数据会设置为NULL。

示例代码:

parent_id = Column(Integer, ForeignKey("parent.id", ondelete="CASCADE"))

一对多

class User(Base):
    __tablename__ = 'user'
    id = Column(Integer,primary_key=True,autoincrement=True)
    username = Column(String(50),nullable=False)

    # articles = relationship("Article")

class Article(Base):
    __tablename__ = 'article'
    id = Column(Integer,primary_key=True,autoincrement=True)
    title = Column(String(50),nullable=False)
    content = Column(Text,nullable=False)

    author = relationship("User",backref="articles")

上述代码表示user--article是一对多的关系,即一个人可以发表多个文章,因此在文章表中添加了author的外键字段关联这user表的主键,提供了一个relationship,这个类可以定义属性,以后在访问相关联的表的时候就直接可以通过属性访问的方式就可以访问得到了。

注意:relationship中的第一个参数是模型名,backref表示反向查询,例如:

  • 多类查询一类
article = session.query(Article).first()
# 通过文章查询作者
user = article.author
print(user.username)
  • 一类查询多类
user = session.query(User).first()
articles = user.articles

backref:主要用作一类查询多类,如果我们不在多类中的relationship使用此参数就需要在一类中定义relationship,例如:

class User(Base):
    __tablename__ = 'user'
    id = Column(Integer,primary_key=True,autoincrement=True)
    username = Column(String(50),nullable=False)
    
   # 关联多类
    articles = relationship("Article")

一对一

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)

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

通过代码可以发现,一对一关系在两个类都定义了relationship,但是在父类参数中多个一个uselist=False,表示一对一关系是一个对象,而不是一个结果集,在一对多过程中默认为True
由于两个类都定义了relationship,同样可以简化到只在子类中定义,代码如下:

from sqlalchemy.orm import backref
class UserExtend(Base):
    ......
    user = relationship("User",backref=backref("extend",uselist=False))

多对多

  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), # article主键
    Column("tag_id", Integer, ForeignKey("tag.id"), primary_key=True)  # tag主键
)

# -----------------------Article----------------------------
class Article(Base):
    __tablename__ = "article"
    id = Column(Integer, primary_key=True, autoincrement=True)
    title = Column(String(50), nullable=False)
    content = Column(Text, nullable=False)

    # 关联字段,二者选其一,此处定义在article表中
    tags = relationship("Tag", backref="articles", secondary=article_tag)

# -----------------------Tag--------------------------------
class Tag(Base):
    __tablename__ = "tag"
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(50), nullable=False)

ORM层面删除数据

ORM层面删除数据,会无视mysql级别的外键约束。直接会将对应的数据删除,然后将从表中的那个外键设置为NULL。如果想要避免这种行为,应该将从表中的外键的nullable=False,阻止为空。

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

user = relationship("User",backref="extend",cascade="save-update,delete") # 用逗号分割多个
  1. save-update:默认选项。在添加一条数据的时候,会把其他和他相关联的数据都添加到数据库中。这种行为就是save-update属性影响的。
user = User(...)
article = Article(...)
article.author = user
session.add(article) # 此处只添加了article,但是user也会随着commit一起添加到数据库
session.commit()
  1. delete:表示当删除某一个模型中的数据的时候,是否也删掉使用relationship和他关联的数据。
  2. delete-orphan:表示当对一个ORM对象解除了父表中的关联对象的时候,自己便会被删除掉。当然如果父表中的数据被删除,自己也会被删除。这个选项只能用在一对多上,不能用在多对多以及多对一上。并且还需要在子模型中的relationship中,增加一个single_parent=True的参数。
class Article(Base):
    ......
    author = relationship("User",backref=backref("articles",cascade="all"),cascade="save-update",single_parent=True,ondelete='')
  1. merge:默认选项。当在使用session.merge,合并一个对象的时候,会将使用了relationship相关联的对象也进行merge操作。
  2. expunge:移除操作的时候,会将相关联的对象也进行移除。这个操作只是从session中移除,并不会真正的从数据库中删除。
  3. all:是对save-update, merge, refresh-expire, expunge, delete几种的缩写。
原文地址:https://www.cnblogs.com/ydongy/p/13158031.html