我正在Ruby on Rails中构建一个应用程序,我将包括我的3个模型(及其迁移脚本),以显示我正在尝试做什么,以及什么不起作用。这是纲要:我的应用程序中的用户属于团队,每个团队可以有多个教练。我希望能够提取适用于用户的教练列表。
例如,用户A可以属于T1和T2队。 T1和T2队可以分别有四个不同的教练,一个教练是共同的。我希望能够通过简单地说:
来拉出教练名单u = User.find(1)
coaches = u.coaches
以下是我的迁移脚本以及模型中的关联。我在设计中做错了吗?我的协会是否正确?
class CreateUsers < ActiveRecord::Migration
def self.up
create_table :users do |t|
t.column :login, :string, :default => nil
t.column :firstname, :string, :default => nil
t.column :lastname, :string, :default => nil
t.column :password, :string, :default => nil
t.column :security_token, :string, :default => nil
t.column :token_expires, :datetime, :default => nil
t.column :legacy_password, :string, :default => nil
end
end
def self.down
drop_table :users
end
end
class CreateTeams < ActiveRecord::Migration
def self.up
create_table :teams do |t|
t.column :name, :string
end
end
def self.down
drop_table :teams
end
end
class TeamsUsers < ActiveRecord::Migration
def self.up
create_table :teams_users, :id => false do |t|
t.column :team_id, :integer
t.column :user_id, :integer
t.column :joined_date, :datetime
end
end
def self.down
drop_table :teams_users
end
end
以下是模型(不是整个文件):
class User < ActiveRecord::Base
has_and_belongs_to_many :teams
has_many :coaches, :through => :teams
class Team < ActiveRecord::Base
has_many :coaches
has_and_belongs_to_many :users
class Coach < ActiveRecord::Base
belongs_to :teams
end
当我试图拉扯教练时会发生这种情况:
u = User.find(1)
=> #<User id: 1, firstname: "Dan", lastname: "Wolchonok">
>> u.coaches
ActiveRecord::StatementInvalid: Mysql::Error: #42S22Unknown column 'teams.user_id' in 'where clause': SELECT `coaches`.* FROM `coaches` INNER JOIN teams ON coaches.team_id = teams.id WHERE ((`teams`.user_id = 1))
这是sql中的错误:
Mysql ::错误:#42S22Unknown列'teams.user_id'在'where子句'中:SELECT coaches
。* FROM coaches
INNER JOIN团队ON coaches.team_id = teams.id WHERE(( teams
。user_id = 1))
我在miss:through子句中遗漏了什么?我的设计完全关闭了吗?有人能指出我正确的方向吗?
答案 0 :(得分:4)
你不能连续两次做一个has_many:它会告诉你它是一个无效的关联。如果你不想像上面那样添加finder_sql,你可以添加一个模仿你想要做的事情的方法。
def coaches
self.teams.collect do |team|
team.coaches
end.flatten.uniq
end
答案 1 :(得分:2)
这更像是一种多对多甚至更多的关系。我只想写一些sql:
has_many :coaches, :finder_sql => 'SELECT * from coaches, teams_users WHERE
coaches.team_id=teams_users.team_id
AND teams_users.user_id=#{id}'
答案 2 :(得分:1)
我不认为ActiveRecord可以处理has_many关系中的两步连接。为了实现这一目标,您必须将用户加入team_users,以及团队和教练。 through选项只允许一次额外的连接。
相反,你必须使用:finder_sql选项并自己写出完整的join子句。这不是世界上最漂亮的东西,但当你尝试做一些与众不同的事情时,它就是ActiveRecord的用法。
答案 3 :(得分:1)
您可以删除用户中的“has_many:coaches,:through =&gt;:teams”行。然后在你的用户模型中手写一个coach方法,如下所示:
def coaches
ret = []
teams.each do |t|
t.coaches.each do |c|
ret << c
end
end
ret.uniq
end
答案 4 :(得分:0)
虽然我喜欢编写SQL,但我不认为它是这种情况下的理想解决方案。以下是我最终在用户模型中所做的事情:
def coaches
self.teams.collect do |team|
team.coaches
end.flatten.uniq
end
def canCoach(coachee)
u = User.find(coachee)
coaches = u.coaches
c = []
coaches.collect do |coach|
c.push(coach.user_id)
end
return c.include?(self.id)
end
我想过一下子就这么做了,但我喜欢从用户对象中返回一个coach对象数组的能力。如果有更好的方法,我很有兴趣看到改进的代码。