Peb
Peb

Reputation: 163

Validation on associated object behaving differently on create and update

I have a issue with a custom validation that checks the number of associated objects. There are two classes, Event and Guest.

When the user creates a new event, he can select some guests to be invited. Upon save, I want to do some check on the number of invited guests.

The validation works fine when updating an existing Event. But upon creation of a new event, the validation fails because self.guests.count returns 0. In the controller, I checked that i the selected guests are passed to the model in the same way: params[:event][:guest_ids].

In case of an update, I can see that an SQL INSERT INTO to update the association table is added to the transaction queue before my validation block is called. But in case of a new event, the SQL INSERT doesn’t show up before the validation fails.

What am I missing here? I was thinking about some workarounds, but I’m sure there is a proper way to implement this.

Rails version is 4.2.6

class Event < ActiveRecord::Base
  has_and_belongs_to_many :guests
  validate :check_guestlist

  def check_guestlist
    guest_count=self.guests.count
    if guest_count < 3
      errors.add(“Please invite more guests”)
    end
  end

class Guest < ActiveRecord::Base
  has_and_belongs_to_many :events
end


class EventsController < ApplicationController
  def create
    @event = current_user.events.new(event_params)

    respond_to do |format|
      if @event.save
        format.html { redirect_to @event, notice: 'Event was successfully created.' }
      else
        flash.now[:alert]='Event not saved.'
        format.html { render action: "new" }
      end
    end
  end

  def update
    @event = current_user.events.find(params[:id])

    respond_to do |format|
      if @event.update_attributes(event_params)
        format.html { redirect_to @event, notice: 'Event was successfully updated.' }
      else
        flash.now[:alert]='Changes not saved.'
        format.html { render action: "edit" }
      end
    end
  end
end

Upvotes: 1

Views: 118

Answers (2)

David
David

Reputation: 3610

Ok the reason why self.guests.count doesn't work on create is that this is actually doing a database query, with the scope of event.

SELECT COUNT(*) FROMguestsWHEREguests.event_id= 1

Now of course this fails because the validation occurs before the guests have been saved. Now on update it works because there are entries to be found in the database. However the validation is checking against the wrong information - the count of guests in the database doesn't necessarily match the number of self.guests - you could have added one more guest, which has yet to be saved.

Your solution does work but personally I think it would be better to write it as:

validates :guests, length: { minimum: 3, message: 'Please invite more guests' }

length will test the size of the guests array - e.g. self.guests.length would be effectively the same as self.guests.to_a.count

Upvotes: 1

Peb
Peb

Reputation: 163

I managed to work around this problem by replacing a line of code in the custom validation. If I replace

    guest_count=self.guests.count

with

    guest_count=self.guests.to_a.count

I get the expected results. In case of an update, both expressions return the same result. But in the context of a new record, only the second one responds as expected. Nevertheless, I still don't understand why the first version didn't work.

Upvotes: 0

Related Questions