Rails 3: проверка наличия по крайней мере одного из них имеет много элементов объединения

У меня две модели: Project и ProjectDiscipline:

class Project < ActiveRecord::Base
  has_many :project_disciplinizations, :dependent => :destroy
  has_many :project_disciplines, through: :project_disciplinizations
  attr_accessible :project_discipline_ids
  attr_accessible :project_disciplines_attributes
  accepts_nested_attributes_for :project_disciplines, :reject_if => proc { |attributes| attributes['name'].blank? }
end

class ProjectDiscipline < ActiveRecord::Base
  attr_accessible :name
  has_many :project_disciplinizations, :dependent => :destroy
  has_many :projects, through: :project_disciplinizations
end

class ProjectDisciplinization < ActiveRecord::Base
  attr_accessible :project_discipline_id, :project_id
  belongs_to :project_discipline
  belongs_to :project
end

В новой/редактируемой форме Project меня есть список дисциплин и флажок для каждого из них, чтобы пользователи могли выбирать дисциплины:

<div class="control-group">
  <label class="control-label">Check disciplines that apply</label>
  <div class="controls">
    <%= f.collection_check_boxes(:project_discipline_ids, ProjectDiscipline.order('name'), :id, :name, {}, {:class => 'checkbox'}) {|input| input.label(:class => 'checkbox') { input.check_box + input.text }} %>
    <p class="help-block">You must choose at least one discipline.</p>
  </div>
</div>

Я хочу добавить проверку, требующую проверки хотя бы одной дисциплины. Я пробовал, но пока не понял. Как добавить эту проверку?

Ответ 1

Прежде чем ответить на вопрос, основываясь на структуре ваших моделей, я бы использовал has_and_belongs_to_many, вместо того, чтобы использовать эту явную модель привязки, поскольку она кажется, что модель связывания не добавляет ничего ценного.

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

validate :has_project_disciplines

def has_project_disciplines
  errors.add(:base, 'must add at least one discipline') if self.project_disciplinizations.blank?
end

или с has_and_belongs_to_many

validate :has_project_disciplines

def has_project_disciplines
  errors.add(:base, 'must add at least one discipline') if self.project_disciplines.blank?
end

Ответ 2

Я предпочитаю более простой подход:

class Project < ActiveRecord::Base
  validates :disciplines, presence: true
end

Этот код абсолютно такой же, как код Джона Наглела или Алекса Пичея, из-за validates_presence_of эксплойта blank? метод тоже.

Ответ 3

Вы можете написать свой собственный валидатор

class Project < ActiveRecord::Base

  validate :at_least_one_discipline

  private
  def at_least_one_discipline
    # Check that at least one exists after items have been de-selected
    unless disciplines.detect {|d| !d.marked_for_destruction? }
      errors.add(:disciplines, "must have at least one discipline")
    end
  end
end