• 数据库进阶实践-级联操作 --


    数据库进阶实践

    级联操作

    Cascade意为“级联操作”,就是在操作一个对象的同时,对相关的对象也执行某些操作。我们通过一个Post模型和Comment模型来演示级联操作,分别表示文章(帖子)和评论,两者是一对多关系:

    class Post(db.Model):
        id = db.Column(db.Integer, primary_key = True)
        title = db.Column(db.String(50), unique = True)
        body = db.Column(db.Text)
        comments = db.relationship('Comment', back_populates = 'post')
        
        
    
    class Comment(db.Model):
        id = db.Column(db.Integer, primary_key = True)
        body = db.Column(db.Text)
        post_id = db.Column(db.Integer, db.ForeignKey('post.id'))
        post = db.relationship('Post', back_populates = 'comments')

    级联行为通过关系函数relationship()的cascade参数设置。我们希望在操作Post对象时,处于附属地位的Comment对象也被相应执行某些操作,这时应该在Post类的关系函数中定义级联参数。设置了cascade参数的一侧将被视为父对象,相关的对象则被视为子对象。

    cascade通常使用多个组合值,级联值之间使用逗号分隔,比如:

    class Post(db.Model):
        …
        comments = db.relationship('Comment', cascade = 'save-update, merge,delete',back_populates = 'post')

     

    常用的配置组合如下所示:

    1)      save-update、merge(默认值)

    2)      save-update、merge、delete

    3)      all

    4)      all、delete-orphan

    当没有设置cascade参数时,会使用默认值save-upgrate、merge。上面的all等同于除了delete-orphan以外所有可用值的组合,即save-update、merge、refresh-expire、expunge、delete。

    下面介绍常用的几个级联值:

    1、save-update

    save-update是默认的级联行为,当cascade参数设为save-update时,如果使用db.session.add()方法将Post对象添加到数据库会话时,那么与Post相关联的Comment对象也将被添加到数据库会话。我们首先创建一个Post对象和两个Comment对象:

    >>> post = Post()
    >>> comment1 = Comment()
    >>> comment2 = Comment()
    将post1添加到数据库会话后,只有post1在数据库会话中:
    >>> db.session.add(post)
    >>> post in db.session
    True
    >>> comment1 in db.session
    False
    >>> comment2 in db.session
    False
    如果我们让post1与这两个Comment对象建立关系,那么这两个Comment对象也会自动被添加到数据库会话中:
    >>> post.comments.append(comment1)
    >>> post.comments.append(comment2)
    >>> comment1 in db.session
    True
    >>> comment2 in db.session
    True

    当调用db.session.commit()提交数据库会话时,这三个对象都会被提交到数据库中。

    2、delete

    如果某个Post对象被删除,那么按照默认的行为,该Post对象相关联的所有Comment对象都将与这个Post对象取消关联,外键字段的值会被清空。如果Post类的关系函数中cascade参数设为delete时,这些相关的Comment会在关联的Post对象删除时一并删除,当需要设置delete级联时,我们会将级联值设为all或save-update、merge、delete,比如:

    class Post(db.Model):
        id = db.Column(db.Integer, primary_key = True)
        title = db.Column(db.String(50), unique = True)
        body = db.Column(db.Text)
        comments = db.relationship('Comment',cascade = 'all', back_populates = 'post')
    
    

    我们先创建一个文章对象post2和两个评论对象comment3和comment4,并将这两个评论对象与文章对象建立关系,将它们添加到数据库会话并提交:

    >>> comment3 = Comment(body = 'very good')
    >>> comment4 = Comment(body = 'excellent')
    >>> post2 = Post(title = 'i have a good plan', body = 'tomorrow i will go to climbing')
    >>> post2.comments.append(comment3)
    >>> post2.comments.append(comment4)
    >>> db.session.add(post2)
    >>> db.session.commit()

    现在共有两条Post记录和四条Comment记录:

    >>> Post.query.all()
    [<Post 1>, <Post 2>]
    >>> Commment.query.all()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    NameError: name 'Commment' is not defined
    >>> Comment.query.all()
    [<Comment 1>, <Comment 2>, <Comment 3>, <Comment 4>]

    如果删除文对象Post2,那么对应的两个评论对象也会一并被删除:

    >>> post = Post.query.get(2)
    >>> post
    <Post 2>
    >>> db.session.delete(post)
    >>> db.session.commit()
    >>> Post.query.all()
    [<Post 1>]
    >>> Comment.query.all()
    [<Comment 1>, <Comment 2>]
    3、delete-orphan

    这个模式是基于delete级联的,必须和delete级联一起使用,通常会设为all、delete-orphan,因为all包含delete。因此当cascade参数设为delete-orphan时,它首先包含delete级联的行为:当某个Post对象被删除时,所有相关的Comment对象都将被删除(delete级联)。除此之外,当某个Post对象(父对象)与某个Comment对象(子对象)解除关系时,也会删除该Comment对象,这个解除关系的对象被称为鼓励对象(orphan object),现在comments属性中的级联值为all、delete-orphan,如下所示:

    class Post(db.Model):
        id = db.Column(db.Integer, primary_key = True)
        title = db.Column(db.String(50), unique = True)
        body = db.Column(db.Text)
        comments = db.relationship('Comment',cascade = 'all, delete-orphan', back_populates = 'post')
     

    我们先创建一个文章对象post3和两个评论对象comment5和comment6,并将这两个评论对象与文章对象建立关系,将他们添加到数据库会话并提交:

    >>> post3 = Post()
    >>> post3 = Post(title = 'today learn python', body = 'python include class and object')
    >>> comment5 = Comment(body = 'i also wanna learn python')
    >>> comment6 = Comment(body = 'python is easy to learn, but you have to pay your time every day')
    >>> post3.comments.append(comment5)
    >>> post3.comments.append(comment6)
    >>> db.session.add(post3)
    >>> db.session.commit())
      File "<stdin>", line 1
        db.session.commit())
                           ^
    SyntaxError: invalid syntax
    >>> db.session.commit()

    现在数据库中有两条文章记录和四条评论记录:

    >>> Post.query.all()
    [<Post 1>, <Post 2>]
    >>> Comment.query.all()
    [<Comment 1>, <Comment 2>, <Comment 3>, <Comment 4>]
    下面我们将comment5和comment6与post3解除关系并提交数据库会话:
    >>> post3.comments.remove(comment5)
    >>> post3.comments.remove(comment6)
    >>> db.session.commit()

    默认情况下,相关评论对象的外键会被设为空值。因为我们设置了delete-orphan级联,所以现在你会发现解除关系的两条评论记录都被删除了:

    >>> Comment.query.all()
    [<Comment 1>, <Comment 2>]

    delete和delete-orphan通常会在一对多关系模式中,而且“多”这一侧的对象附属于“一”这一侧的对象时使用。尤其是如果“一”这一侧的“父”对象不存在了,那么“多”这一侧的“子”对象不再有意义的情况。比如,文章和评论的关系就是一个典型的示例。当文章被删除了,那么评论也就没必要在留存。在这种情况下,如果不使用级联操作,那么我们就需要手动迭代关系另一侧的所有评论对象,然后一一进行删除操作。

    对于这两个级联选项,如果你不会通过列表语义对集合关系属性调用remove()方法等方式来操作关系,那么使用delete级联即可。

    虽然级联操作方便,但是容易带来安全隐患,因此要谨慎使用。默认值能够满足大部分情况,所以最好仅在需要的时候才修改它。

    在SQLAlchemy中,级联的行为和配置选项等最初衍生自另一个ORM—Hibernate ORM。如果对这部分内容感到困惑,那么引用SQLAlchemy文档中关于Hibernate文档的结论:“The sections we have just covered can be a bit confusing.However, in practice, it all works out nicely. (我们刚刚介绍的这部分内容可能会有一些让人困惑,不过在实际使用中,他们都会工作的很顺利)”

  • 相关阅读:
    js中取整数的方法
    js中原型和原型链
    js中获取class封装
    Cocos2dx打包成apk包时在手机上闪退
    计算两个日期间相差的天数
    Stealing a Cake [函数方程符合凸性 三分]
    插件式架构设计(转)
    转:Entity FrameWork利用Database.SqlQuery<T>执行存储过程并返回参数
    后台web请求代码(含https,json提交)
    sitecore 缓存管理器
  • 原文地址:https://www.cnblogs.com/xiaxiaoxu/p/10703364.html
Copyright © 2020-2023  润新知