weird stuff: validate_uniqueness_of with :scope does not work

Running rails 2.3.8.

I have a two models defined like this

class Member   has_many :folders   before_save: create_default_folders

protected   def create_default_folders!     %w{Inbox Sent}.each do |folder|   unless self.folders.find_by_name(folder)     self.folders.create!(:name => folder, :deletable => false)   end     end   end end

class Folder   belongs_to :member   # folder names must be unique for each user   validates_uniqueness_of :name, :scope => [:member_id], :allow_blank => false,       :case_sensitive => false, :message => 'already exists for this member' end

------ MembersController def create     @member = Member.new(params[:member])

    if @member.save          ...     end end

So as you can see, what I wish to get when I save a new Member are two default folders colled (Inbox and Sent). So I defined the after_save callback. I tested it in script/console and it works whenever I build and save a Member.

However, as soon as I create a member through a controller-backed form, the uniqueness validation gets tripped up. I get validation error from my validates_uniqueness_of as "Folder already exists for this member". Which shouldn't happen.

For some strange reason :scope gets ignored and thus uniqueness check isn't properly scoped by member_id...

The weird part is that it all works in script/console, but not via web forms.

Any suggestions???

Thanks in advance!

However, as soon as I create a member through a controller-backed form, the uniqueness validation gets tripped up. I get validation error from my validates_uniqueness_of as "Folder already exists for this member". Which shouldn't happen.

For some strange reason :scope gets ignored and thus uniqueness check isn't properly scoped by member_id...

The weird part is that it all works in script/console, but not via web forms.

Is there some odd interaction because you're creating these folders from a before_save (ie the member's id has not yet been set)?

Fred

dont use the before_save use transactions that way it will roll back both actions if there is an error. and dont use that function you have with the each and all use

find_or_create_by_name(name)

it does what you want.

That's what I thought originally about id not being set... but why does it work from script/console ? That kind of puzzles me...

That code must've been 5th iteration of trying to get that call back to work.

I've tried your suggested method. I've so tried create/create!, save/save!.

Everything work from console. That is I can do m = Member.new m.x = "value" m.folders.find_or_create_by_name('folder_name") m.save

It all work beautifully from console. I can test my validation, it works.

But as soon as I do the same thing (create new Member) from controller context via form params, the scoped validates_uniqueness_of does not function as expected.

Also note that I have to set at least one other folder attribute while creating a folder , so this variant doesn't help me much.

check your attri_accesible, and check if you rae using a gem that uses attr_protected, it appears you have a problem with mass assignment and you cant see from one controller the attribute that member_id from the folder controller i think you need to be able to , also note that with before save there is no member_id in the database to compare the validation with thats why i said you should use a transaction and create everything from the member controller like this

Member.transaction do @member.new(blah, blah) @member.save! params[:folder_name].nil? ? name= “Default” : name = params[:folder_name] @folder = Folder.find(:conditions =>{:name = name, :member_id=> @member.id}) @folder ||= Folder.new(blah blah) @folder.member = @member @folder.save! end

try that, the code inside the transaction will rollback if either to the save fail and everything will go back to the its original state.

I've created a brand new Rails 2.3.8 app. No gems, nothing.

I created only the bare necessities: Two models (Member and Folder) one controller (MembersController with only new and create actions) one view (new).

I can create one member with default folders as per my callback (the very first one), but any subsequent members don't get default folders created for them.

But again, when I try the same thing from script/console everything works (I create new member and, bam, two folders come into existence as well)..

w t f....

watch this and see if ti helps,

http://railscasts.com/episodes/196-nested-model-form-part-1