如何向 Pivot 添加附加信息(使用 Fluent)?

Posted

技术标签:

【中文标题】如何向 Pivot 添加附加信息(使用 Fluent)?【英文标题】:How do I add additional information to a Pivot (using Fluent)? 【发布时间】:2016-11-12 17:24:47 【问题描述】:

在 Vapor 中,我们可以通过创建 Pivot<U, T> 对象来创建多对多关系,其中 UT 是我们想要链接在一起的模型。所以如果我想创建一个Users 可以有很多Files 并且很多Files 可以属于很多Users 的系统,我会这样关联它们:

var alice = User(name: "Alice")
try! alice.save()
var sales = File(name: "sales.xclx")
try! sales.save()

var pivot = Pivot<User, File>(alice, sales)
try! pivot.save()

我一生都想不通的是,如何让Pivot&lt;User, File&gt; 包含其他信息?例如,我想知道这个文件是什么时候与 Alice 关联的,或者她对它有什么权限。

在关系数据库上,Fluent 为 Pivot&lt;User, File&gt; 类型创建此表。

+---------+---------+------+-----+---------+----------------+
| Field   | Type    | Null | Key | Default | Extra          |
+---------+---------+------+-----+---------+----------------+
| id      | int(11) | NO   | PRI | NULL    | auto_increment |
| file_id | int(11) | NO   |     | NULL    |                |
| user_id | int(11) | NO   |     | NULL    |                |
+---------+---------+------+-----+---------+----------------+

但我希望能够代表这样的东西:

+---------+---------+------+-----+---------+----------------+
| Field   | Type    | Null | Key | Default | Extra          |
+---------+---------+------+-----+---------+----------------+
| id      | int(11) | NO   | PRI | NULL    | auto_increment |
| file_id | int(11) | NO   |     | NULL    |                |
| user_id | int(11) | NO   |     | NULL    |                |
| date    | date    | NO   |     | NULL    |                |
| perms   | varchar | NO   |     | READ    |                |
+---------+---------+------+-----+---------+----------------+

【问题讨论】:

【参考方案1】:

Pivot&lt;U, T&gt; 对象可以被认为是像siblings 这样的透视关系的“最低限度”必需字段。

如果您想在此表中添加自定义字段,您可以创建自己的类来充当枢轴,只要它具有所需的元素:

FooBar 的表名称为 bar_foo(小写,按字母顺序排列) 至少存在三列:idbar_idfoo_id

换句话说,您的数据透视类创建的表必须至少包含 Pivot&lt;Foo, Bar&gt; 准备创建的元素。

完成此操作后,您可以通过创建和保存枢轴类的实例来创建新的枢轴关系。

当在使用此数据透视表的模型上调用 .siblings() 关系时,仍将创建默认的 Pivot&lt;U, T&gt; 来执行提取。但是,这不会产生任何问题,因为数据透视表上存在必填字段。

【讨论】:

我认为这是一个很好的答案,但仍然存在一些问题。你说创建一个新类作为一个支点,这是否意味着我应该对它进行子类化?如果您留下一个小例子,我将不胜感激。 不能继承它,因为它不是open。做:class MyPivot: Model 嗯,这确实回答了我必须基于模型创建自己的 Pivot,而不是继承 Pivot。我想我会进一步探索这个想法,看看它是如何进行的。 如果我们要创建模型以便使用 Pivot 函数,我们将不得不修改数据库中的表名,因为会有一个复数形式的 s,并且 Pivot 创建一个连接表名而没有 @ 987654335@在最后【参考方案2】:

因此,在遇到 Andy 描述的相同问题并在 Vapor Slack 上寻求解决方案后,我被重定向到这里。

我对 Tanner 提出的解决方案的实现(使用 PostgreSQL)可以找到here

关键是Rating模型:

这是一个普通的 Model 子类 它的 entity 名称为 movie_user(如 Tanner 所述,相关模型的名称按字母顺序排列) 它有userId(映射到"user_id")和movieId(映射到"movie_id")字段,两者都是Node类型。 在prepare(Database) 中,它再次使用名称"movie_user" 并将Id 字段定义为Ints。

通过该设置,您可以定义以下关系便利方法:

Movie:所有评分者

extension Movie 
    func raters() throws -> Siblings<User> 
        return try siblings()
    

User:所有分级电影

extension User 
    func ratedMovies() throws -> Siblings<Movie> 
        return try siblings()
    

可以像这样添加电影的新评级(针对用户):

ratings.post()  request in
    var rating = try Rating(node: request.json)
    try rating.save()
    return rating

由于Rating 是模型子类,我们可以直接从请求 JSON 中创建它。这就需要客户端发送一个符合Rating类节点结构的JSON文档:


    "user_id": <the rating users id>,
    "movie_id": <the id of the movie to be rated>,
    "stars": <1-5 or whatever makes sense for your usecase>

要获得给定电影的所有实际Ratings,您似乎必须手动解决关系(至少我认为是这样,也许有人可以给我一个关于如何更好地做到这一点的提示):

let ratings = try Rating.query().filter("movie_id", movieId).all()

此外,目前似乎无法以某种方式计算数据库的平均值。我会喜欢这样的工作:

// this is now how it works
let averageRating = try Rating.query().filter("movie_id", movieId).average("stars")

所以我希望这可以帮助任何遇到这个问题的人。感谢所有为 Vapor 项目做出贡献的优秀人士!

感谢@WERUReo 指出创建评级的部分缺失。

【讨论】:

我还有一个问题是,你如何添加评级?我看到有一个用于评分的 POST 路由,但是您需要传递什么 JSON 才能为来自特定用户的特定电影添加评分? 感谢您指出这一点。我更新了答案。基本上,您只是发布整个关系:user_idmovie_idstars。但这只是一种方法。你也可以选择POST /users/&lt;user_id&gt;/movies/&lt;movie_id&gt; "stars": 4(或其他方式),然后在请求处理程序中组装关系。

以上是关于如何向 Pivot 添加附加信息(使用 Fluent)?的主要内容,如果未能解决你的问题,请参考以下文章

向区域添加附加信息。信标

如何向 matplotlib 注释添加附加文本

如何在保留书签的同时向现有 pdf 添加附加页面? (PDFSharp等)

如何向 Django QuerySet 添加附加列

如何向多个子图添加附加图

在 MongoDB (rmongodb) 中附加 BSON 数组