Как связать новую модель с существующими моделями, используя has_and_belongs_to_many - PullRequest
6 голосов
/ 21 января 2010

У меня есть две модели с отношением "многие ко многим", использующими has_and_belongs_to_many. Вот так:

class Competition < ActiveRecord::Base
  has_and_belongs_to_many :teams
  accepts_nested_attributes_for :teams
end

class Team < ActiveRecord::Base
  has_and_belongs_to_many :competitions
  accepts_nested_attributes_for :competitions
end

Если мы предположим, что я уже создал несколько соревнований в базе данных, при создании новой команды я хотел бы использовать вложенную форму, чтобы связать новую команду с любыми соответствующими соревнованиями.

Именно в этот момент мне действительно нужна помощь (я застрял на ней часами!), И я думаю, что мой существующий код уже сделал это неправильно, но я покажу это на всякий случай:

class TeamsController < ApplicationController
  def new
    @team = Team.new
    @competitions.all
    @competitions.size.times {@team.competitions.build}
  end
  def create
    @team = Team.new params[:team]
    if @team.save
      # .. usual if logic on save
    end
  end
end

И мнение ... это то, где я действительно застрял, так что я пока не буду публиковать свои усилия. Мне бы хотелось, чтобы это был список флажков для каждого соревнования, чтобы пользователь мог просто выбрать, какие соревнования являются подходящими, и оставить непроверенными те, которые не подходят.

Я действительно застрял с этим, поэтому ценим любое указание в правильном направлении, которое вы можете предоставить:)

1 Ответ

4 голосов
/ 21 января 2010

Метод has_and_belongs_to_many для объединения моделей не рекомендуется в пользу нового подхода has_many ...: through. Управлять данными, хранящимися в отношении has_and_belongs_to_many, очень сложно, так как Rails не предоставляет методов по умолчанию, но метод: through является первоклассной моделью и может использоваться как таковой.

Что касается вашей проблемы, вы можете решить ее следующим образом:

class Competition < ActiveRecord::Base
  has_many :participating_teams
  has_many :teams,
    :through => :participating_teams,
    :source => :team
end

class Team < ActiveRecord::Base
  has_many :participating_teams
  has_many :competitions,
    :through => :participating_teams,
    :source => :competition
end

class ParticipatingTeam < ActiveRecord::Base
  belongs_to :competition
  belongs_to :team
end

Когда речь идет о создании самих команд, вы должны структурировать свою форму так, чтобы один из полученных вами параметров был отправлен в виде массива. Как правило, это делается путем указания того, что все поля флажков имеют одинаковое имя, например «соревнования []», а затем для каждого флажка устанавливается значение, равное идентификатору соревнования. Тогда контроллер будет выглядеть примерно так:

class TeamsController < ApplicationController
  before_filter :build_team, :only => [ :new, :create ]

  def new
    @competitions = Competitions.all
  end

  def create
    @team.save!

    # .. usual if logic on save
  rescue ActiveRecord::RecordInvalid
    new
    render(:action => 'new')
  end

protected
  def build_team
    # Set default empty hash if this is a new call, or a create call
    # with missing params.
    params[:team] ||= { }

    # NOTE: HashWithIndifferentAccess requires keys to be deleted by String
    # name not Symbol.
    competition_ids = params[:team].delete('competitions')

    @team = Team.new(params[:team])

    @team.competitions = Competition.find_all_by_id(competition_ids)
  end
end

Установка статуса отмеченного или не отмеченного для каждого элемента в вашем списке флажков выполняется чем-то вроде:

checked = @team.competitions.include?(competition)

Где «соревнование» - это то, что повторяется.

Вы можете легко добавлять и удалять элементы из вашего списка соревнований, или просто переназначить весь список, и Rails определит новые отношения на его основе. Ваш метод обновления не будет отличаться от нового метода, за исключением того, что вы будете использовать update_attributes вместо new.

...