How to handle validation of Has_Many Through associations

1,386 views
Skip to first unread message

Adil Wali

unread,
Feb 20, 2013, 3:51:07 PM2/20/13
to fabrica...@googlegroups.com
Hey again.  We have one small issue that we're having trouble with.  We have a has_many through relationship that we validate presence of.  Even when using the "after_build" callback, the validation seems to fail.  Here's the relevant code:

Model
class User < ActiveRecord::Base
  has_many :user_roles
  has_many :roles, through: :user_roles
  validates :first_name, :last_name, :user_roles, presence: true
end

class UserRole < ActiveRecord::Base
  belongs_to :user
  belongs_to :role
end

class Role < ActiveRecord::Base
  has_many :user_roles
  has_many :users, through: :user_roles
end

Fabricator
Fabricator(:user) do
  first_name "Person"
  last_name "McFace"
  email {...}
  paypal_email {...}
  password "woah-plain-text"
  rate { (30..50).to_a.sample }
  after_build do |user|
    Fabricate(:user_role, :user => user, :role => Fabricate(:consultant_role))
  end
end

Error
ActiveRecord::RecordInvalid: Validation failed: User roles can't be blank

The above sort of makes sense.. because when it tries to insert the 'user_role' into the database, the 'user_id' is actually nil at that point (because the user wasn't created yet.)  

I've generated the same data in ActiveRecord by using 'user_role_attributes'.  Like so:
user = User.create! {...}, user_roles_attributes: [ role_id: Role.consultant.id ]

That works, but I don't know how to accomplish something similar in Fabrication.  

Thanks for any help/insight you can provide!


Paul Elliott

unread,
Feb 20, 2013, 4:08:48 PM2/20/13
to fabrica...@googlegroups.com
Have you tried building the role and associating it instead of explicitly building the join table record? I have found that in the case of a has_many :through, it is best to ignore the fact that there is a table in the middle in your application code.

Fabricator(:consultant, from: :user) do
  roles(count: 1) { Fabricate.build(:consultant_role) }
end


-- Paul

--
You received this message because you are subscribed to the Google Groups "fabrication" group.
To unsubscribe from this group and stop receiving emails from it, send an email to fabricationge...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Adil Wali

unread,
Feb 20, 2013, 4:13:04 PM2/20/13
to fabrica...@googlegroups.com
Paul:  First, you are AMAZING with the response time.  I really appreciate it.  Second, it's funny you say that.  That's what the code used to be.  My diff screenshot is attached.  

I was trying this after_build thing since that wasn't working. 

--AW
Screenshot_2_20_13_1_11_PM.png

Paul Elliott

unread,
Feb 20, 2013, 9:04:09 PM2/20/13
to fabrica...@googlegroups.com
Have you tried changing the validation to :roles instead of :user_roles? Generally I wouldn't refer to a join table anywhere other than in the :through option. Everywhere else you should be referring to the other end of the has_many as if it was not a through.

-- Paul

Adil Wali

unread,
Feb 27, 2013, 9:45:41 AM2/27/13
to fabrica...@googlegroups.com
Great idea.  I knew I had something wrong on the rails end.  Validating the endpoint rather than the join table just feels better. 

With that said, I set a value on the join table that I also needed for my test.  Something like 'join-strength-value= integer'

The simplest way to address with Fabricator was to make the user with:
user = Fabricate.build(:User)
user << user_role

Hope that helps anyone else that has a similarly sticky problem.

Reply all
Reply to author
Forward
0 new messages