天天看点

用 Flask 来写个轻博客 (7) — (M)VC_models 的关系(many to many)目录前文列表扩展阅读前期准备多对多使用样例一直在使用的 session

<a href="#%E7%9B%AE%E5%BD%95">目录</a>

<a href="#%E5%89%8D%E6%96%87%E5%88%97%E8%A1%A8">前文列表</a>

<a href="#%E6%89%A9%E5%B1%95%E9%98%85%E8%AF%BB">扩展阅读</a>

<a href="#%E5%89%8D%E6%9C%9F%E5%87%86%E5%A4%87">前期准备</a>

<a href="#%E5%A4%9A%E5%AF%B9%E5%A4%9A">多对多</a>

<a href="#%E4%BD%BF%E7%94%A8%E6%A0%B7%E4%BE%8B">使用样例</a>

<a href="#%E4%B8%80%E7%9B%B4%E5%9C%A8%E4%BD%BF%E7%94%A8%E7%9A%84-session">一直在使用的 session</a>

<a href="http://blog.csdn.net/jmilk/article/details/53150084">用 Flask 来写个轻博客 (1) — 创建项目</a>

<a href="http://blog.csdn.net/jmilk/article/details/53152158">用 Flask 来写个轻博客 (2) — Hello World!</a>

<a href="http://blog.csdn.net/jmilk/article/details/53153382">用 Flask 来写个轻博客 (3) — (M)VC_连接 MySQL 和 SQLAlchemy</a>

<a href="http://blog.csdn.net/jmilk/article/details/53184903">用 Flask 来写个轻博客 (4) — (M)VC_创建数据模型和表</a>

<a href="http://blog.csdn.net/jmilk/article/details/53187575">用 Flask 来写个轻博客 (5) — (M)VC_SQLAlchemy 的 CRUD 详解</a>

<a href="http://blog.csdn.net/jmilk/article/details/53229180">用 Flask 来写个轻博客 (6) — (M)VC_models 的关系(one to many)</a>

<a href="http://blog.csdn.net/jmilk/article/details/52445093">SQLAlchemy_定义(一对一/一对多/多对多)关系</a>

<a href="http://blog.csdn.net/jmilk/article/details/52662797">Openstack_SQLAlchemy_一对多关系表的多表插入实现</a>

在实现多对多之前,我们还需要先增加一个评论(Comment) models class,而且 Comment 是 Post 的关系是 one to many。

首先依旧是在 models.py 中定义模型类:

然后要记住在 manage.py 中返回 Comment

最后在 manager shell 验证是否成功导入了 Comment

如果我们有两个 models,它们之间是相互引用的,而且彼此都可以相互引用对方的多个对象。这就是所谓 many to many 的关系类型。

多对多关系会在两个类之间增加一个关联表。 这个关联的表在 <code>relationship()</code> 方法中通过 secondary 参数来表示。通常的,这个表会通过 MetaData 对象来与声明基类关联, 所以这个 ForeignKey 指令会使用链接来定位到远程的表:

many to many 的关系仍然是由 <code>db.relationship()</code> 来定义

seconddary(次级):会告知 SQLAlchemy 该 many to many 的关联保存在 posts_tags 表中

backref:声明表之间的关系是双向,帮助手册 <code>help(db.backref)</code>。需要注意的是:在 one to many 中的 backref 是一个普通的对象,而在 many to many 中的 backref 是一个 List 对象。

NOTE 1:实际上 db.Table 对象对数据库的操作比 db.Model 更底层一些。后者是基于前者来提供的一种对象化包装,表示数据库中的一条记录。 posts_tags 表对象之所以使用 db.Table 不使用 db.Model 来定义,是因为我们不需要对 posts_tags (self.name)进行直接的操作(不需要对象化),posts_tags 代表了两张表之间的关联,会由数据库自身来进行处理。

NOTE 2: posts_tags 的声明定义最好在 Post 和 Tag 之前。

NOTE 3: 没添加一个 models class 都要记得在 manage.py 中导入并返回,方便之后的调试,这里就不作重复了。

同步数据库

NOTE: 此时的数据库中还是只有原来就已经存在的两条 posts 记录,但是还没有 tags 记录。这是因为在刚刚实例化的 Tag 对象还没有被提交,所以不会被写入到数据库中。

NOTE:再次提交 post_one/post_two 对象。post_one/post_two 对象相应的记录本就已经存在于数据库中了为什么要重新提交呢?

这是因为 post_one/post_two 都被指定了新的关联属性 tags,所以提交 post_one/post_two 不仅仅是更新 posts 的引用,更重要的是将新创建的 3 个 tags 对象写入到数据库中,同时也是将 posts 和 tags 的映射关系写入到 posts_tags 表中。

再次查看数据库:

NOTE:在上面说过了 many to many 的 backref 是一个 List 对象,所以我们还可以反过来为 tags 添加一个 posts 对象(引用)。

再次查看数据库的记录:

NOTE: 从 posts_tags 的记录中可以看出 posts 和 tags 之间的多对多关系。

获取文章 First Post 下有哪些标签:

获取标签 JmilkFan 下有哪些文章

NOTE:再次说明一下,在定义 models 间关系时使用的 <code>backref</code> 参数,指定了加载关联对象的方式(这里使用了动态方式),所以加载 Tag 的关联对象 Post 时,返回的是 <code>sqlalchemy.orm.dynamic.AppenderBaseQuery object</code> 而不是全部的关联对象。

那么为什么反之却是直接返回全部的关联对象呢?

这是因为我们是在 Post 中使用了 <code>backref</code> 对象,所以对于两者的关系而言,backref 指的是 Tag。

session 是连接到数据库的一个桥梁,实际上 session 具有更多的功能,例如:事务。

事务:是对数据库进行操作即集合,在我们 commit 的时候,实际事务帮我们实现了一系列有效的数据库操作。

例如:刚刚我们在 commit 一个 post_one/post_two 前,明明没有 commit tag_one/tag_two/tag_three,为什么数据库中还会写入这三条记录呢?这些都是由事务去帮我们进行的隐式的数据库操作。如果没有事务我们就需要按部就班一步步的完成对数据库的写入,这样的效率是非常低的。除此之外 SQLAlchemy 的 session 还会提供很多有用的功能,感兴趣的话可以继续挖掘,这里就不多做介绍了。