Reputation: 163
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
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(*) FROM
guestsWHERE
guests.
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
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