Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails 3: validate presence of at least one has many through association item

I have two models: Project and 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

On the new/edit Project form, I have a list of disciplines and a check box for each one of them so users can pick disciplines:

<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>

I want to add a validation to require that at least one discipline is checked. I've tried but I haven't figured out yet. How can I add this validation?

like image 647
leonel Avatar asked Jul 23 '13 14:07

leonel


3 Answers

Side note before the answer, based on the structure of your models I would use has_and_belongs_to_many instead of using this explicit linking model since it appears the linking model doesn't add anything of value.

Either way though, the answer is the same, which is to use a custom validation. Depending on whether you go with things the way they are or simplify to a has_and_belongs_to many you'll want to validate slightly differently.

validate :has_project_disciplines

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

or with 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
like image 136
Alex Peachey Avatar answered Oct 22 '22 03:10

Alex Peachey


I prefer simpler approach:

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

This code do absolutely the same as code by John Naegle or Alex Peachey, because of validates_presence_of exploit blank? method too.

like image 44
asiniy Avatar answered Oct 22 '22 04:10

asiniy


You can write your own validator

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
like image 44
John Naegle Avatar answered Oct 22 '22 04:10

John Naegle