2008-10-13 2 views
4

Я создаю приложение в Ruby on Rails, и я включаю 3 из моих моделей (и их скрипты миграции), чтобы показать, что я пытаюсь сделать, и что не работает. Вот краткое изложение: у меня есть пользователи в моем приложении, которые принадлежат командам, и каждая команда может иметь несколько тренеров. Я хочу получить список тренеров, которые применимы к пользователю.Вопрос ассоциации Activerecord: получение has_many: до работы

Например, Пользователь 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 :: Ошибка: # 42S22Неизвестный столбец 'teams.user_id' в 'where clause': SELECT coaches. * FRO M coaches INNER JOIN команды ON coaches.team_id = teams.id WHERE ((teams .user_id = 1))

Я пропустил что-то в своем: через предложение? Мой дизайн полностью отключен? Может ли кто-нибудь указать мне в правильном направлении?

ответ

4

Вы не можете сделать has_many: через два раза подряд. Это скажет вам, что это недействительная связь. Если вы не хотите добавлять finder_sql, как описано выше, вы можете добавить метод, который имитирует то, что вы пытаетесь сделать.

def coaches 
    self.teams.collect do |team| 
     team.coaches 
    end.flatten.uniq 
    end 
1

Я не думаю, что ActiveRecord может справиться с выполнением двухступенчатого соединения в отношениях has_many. Чтобы это сработало, вам придется присоединяться к пользователям команды team_users командам для тренеров. Опция сквозного доступа допускает только одно дополнительное соединение.

Вместо этого вам придется использовать параметр: finder_sql и самостоятельно выписать предложение о полном соединении. Не самая красивая вещь в мире, но так оно и происходит с ActiveRecord, когда вы пытаетесь сделать что-то необычное.

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}' 
1

Вы можете опустить «HAS_MANY: тренеры,: через =>: команды» линии у пользователей & затем вручную написать метод тренеров в модели пользователя следующим образом:

def coaches 
    ret = [] 
    teams.each do |t| 
    t.coaches.each do |c| 
     ret << c 
    end 
    end 
    ret.uniq 
end 
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 

Я думал просто делать все это одним махом, но мне понравилась возможность возвращать массив объектов тренера внутри объекта пользователя.Если есть лучший способ сделать это, я очень заинтересован в улучшении кода.

+0

Убедитесь, что вы используете «uniq» после «flatten», поэтому вы не получаете дубликатов тренеров. – flicken 2008-10-14 14:17:57

Смежные вопросы