Patrick
Patrick

Reputation: 8063

Rails 3 - Validation with 2 models and additional warnings?

I have two models:

The lecture has a capacity and a waiting list. If there's an enrollment for a lecture, I'd like to validate if there are free seats available.

Created two helpers for that:

def availableSeats
  return self.capacity - self.enrollments.confirmedEnrollments.count
end

def waitListAvailable
  return self.waitListCapacity - self.enrollments.waitList.count
end 

I thought about having the checks in the enrollment-controller, but it doesn't work.

if(@lecture.availableSeats <= 0)
  if(@lecture.waitListAvailable <= 0)
    flash[:error] = "Enrolment not possible as the waiting list is full."
    # interrupt and don't save, but how?
  else
    flash[:warning] = "You are on the waiting list."
    @enrollment.confirmed = nil
  end
else
  @enrollment.confirmed = DateTime.now
end

Any ideas how this would work?

Upvotes: 0

Views: 454

Answers (3)

Patrick
Patrick

Reputation: 8063

Thanks edgerunner for the answer! I found another, easy solution:

validate do |enrollment|
  if(enrollment.lecture.availableSeats <= 0)
    enrollment.errors.add_to_base("This lecture is booked out.") if enrollment.lecture.waitListAvailable <= 0
  end
end

The warnings for the waiting list are handled in the controller:

if @enrollment.save
  if @enrollment.confirmed?
    format.html { redirect_to(@lecture, :notice => 'Enrollment successfull.') }
    format.xml  { render :xml => @lecture, :status => :created, :location => @lecture }
  else
    format.html { redirect_to(@lecture, :alert => 'You're on the waiting list!') }
    format.xml  { render :xml => @lecture, :status => :created, :location => @lecture }
  end

Upvotes: 0

edgerunner
edgerunner

Reputation: 14973

I'm assuming that your Enrollment model defines both the accepted students and those on the waiting list. I'm also assuming that the Lecture model has two attributes available_seats and available_wait_space, and the wait list is populated on a first-come basis and students are declined if the list is full, but the actual seats are confirmed or rejected by the lecturer manually.

I'd certainly advise against doing anything at the controller level. This is a job for the models only.

class Enrollment < ActiveRecord::Base
  belongs_to :student
  belongs_to :lecture

  validates_presence_of  :student_id, :lecture_id, :status
  validates_inclusion_of :status, :in => %w[waiting confirmed rejected]
  validate :must_fit_in_wait_list, :on => :create
  validate :must_fit_in_class,     :on => :update

  scope :waiting,   where(:status => 'waiting')
  scope :confirmed, where(:status => 'confirmed')
  scope :rejected,  where(:status => 'rejected')

  def must_fit_in_wait_list
    unless waiting.count < lecture.available_wait_space
      errors.add(:base, "The waiting list is full")
    end
  end

  def must_fit_in_class
    unless confirmed.count < lecture.available_seats
      errors.add(:status, "The seats are full")
    end
  end
end

By the way, don't forget to set the default value for status to "waiting" in your migrations.

Upvotes: 1

zzawaideh
zzawaideh

Reputation: 2011

I think this is best handled in the model. I've had a similar issue in a question I posed previously

Validating :inclusion in rails based on parent Model's attribute value

Upvotes: 0

Related Questions