使用ActiveRecord加入“有很多通过”关联

时间:2013-01-19 10:24:25

标签: sql ruby-on-rails has-many-through rails-activerecord

使用API​​中的导出数据

我正在构建一个排行榜,显示每个团队的Team.name以及选择该特定团队作为收藏的用户。我还填充了另一个属性Favorite.points;显示为相应团队积累的积分最多的用户。

以下是我正在使用的模型:

Favorite.rb

class Favorite < ActiveRecord::Base
  belongs_to :users
  belongs_to :teams
end

Team.rb

class Team < ActiveRecord::Base
  has_many :favorites
  has_many :users, :through => :favorites 
end

User.rb

class User < ActiveRecord::Base
  has_many :favorites
  has_many :teams, :through => :favorites 
end

要开始此过程,我正在尝试匹配Team.external_id和Favorite.team_id之间常见的ID(User.external_id =&gt; Favorites.user_id也是如此)。我可以使用Team.find_all_by_external_id(3333)来获取具有external_id为“3333”的所有Team对象的ID,并且对于Favorite.find_all_by_team_id也是如此。

对于我来说,获取/显示我正在寻找的数据的下一个最佳步骤是什么? SQL连接子句最好吗?或者编写if语句匹配值并迭代JSON数组是否更好?

感谢任何帮助,谢谢!

1 个答案:

答案 0 :(得分:2)

这将为您提供team_idexternal_id表中某一行的teams属性匹配的所有收藏夹,针对特定团队(此处为ID为{{1的团队) }}):

3333

正如我在评论中提到的,这里的棘手问题在于,当您匹配Favorite.joins("left outer join teams on teams.external_id = favorites.team_id")\ .where('team_id' => 3333) 模型上的外部 ID时,您将完全违背rails关联。已创建的属性)Team模型上的team_id(在整个rails中用于获取和分配关联)。

一旦您尝试让团队获得您在上述联接中找到的收藏,您就会看到问题:

Favorite

这里发生了什么?如果仔细查看查询,您会看到rails正在选择f = Favorite.joins("left outer join teams on teams.external_id = favorites.team_id")\ .where('team_id' => 3333).first => #<Favorite id: 1, user_id: nil, team_id: 3333, points: nil, created_at: ... > f.team Team Load (0.3ms) SELECT "teams".* FROM "teams" WHERE "teams"."id" = 3333 LIMIT 1 => nil 为3333的团队。请注意,正在寻找外部的团队id是3333,这就是你想要的。

根本问题在于您尝试使用外部ID(特定于您的API的ID)进行关联,这将无法正常工作。事实上,没有理由这样做。

相反,试试这个:

id

这将为您提供其团队拥有外部ID 3333的所有收藏夹。请注意,Rails将通过加入Favorite.joins(:team).where('teams.external_id = 3333') 然后teams.id = favorites.team_id过滤来实现此目的:

teams.external_id

你可以用相反的方式做同样的事情:

SELECT "favorites".* FROM "favorites" INNER JOIN "teams"
  ON "teams"."id" = "favorites"."team_id" WHERE (teams.external_id = 3333)

将生成SQL:

Team.joins(:favorites).where('teams.external_id = 3333')

再次注意,它是在联接中使用的SELECT "teams".* FROM "teams" INNER JOIN "favorites" ON "favorites"."team_id" = "teams"."id" WHERE (teams.external_id = 3333) ,而不是外部ID。这是执行此操作的正确方法:对您的关联使用传统的id,然后只需在您的(自定义的,API特定的)外部ID的位置进行过滤。

希望有所帮助!

<强>更新

根据评论,似乎您的id模型上的team_id是根据API数据定义的,这意味着该ID对应于您{{1}的Favorite模型。这是一个坏主意:在rails中,外键external_idTeam<model name>_id等)具有特定含义:id被理解为映射到{{1}相应关联模型的字段(team_id)。

要使您的关联起作用,您需要将ID(不是外部ID)用于所有的关联(以及您的user_id模型)。为此,您需要将API中定义的关联转换为rails应用程序中的ID。当您从API添加收藏夹时,请找到与API团队ID相对应的团队id

Team

因此,您要为团队的User分配给定的外部ID。您需要在数据库中查询从API加载的每个收藏夹,这在性能方面可能代价高昂,但是因为只有在没有大问题时才这样做。