Rails: has_one and belongs_to with presence validation on both foreign keys Rails: has_one and belongs_to with presence validation on both foreign keys ruby-on-rails ruby-on-rails

Rails: has_one and belongs_to with presence validation on both foreign keys


When model A has_one model B, this means that B stores the foreign key into A, in the same way that model C has_many model D means that D stores the foreign key into C. The has_one relation simply expresses your desire to allow only one record in B to hold a particular foreign key into A. Given that, you should get rid of user_profile_id from the users schema, because it isn't used. Only user_id from UserProfile is used.

You can still have User check for the presence of UserProfile, but use validates_presence_of :user_profile instead. This will check that the user object has an associated user_profile object.

Your UserProfile object should not check directly for a user_id since this id won't yet exist when creating a new user-user_profile pair. Instead use validates_presence_of :user, which will check that the UserProfile has an associated User object before saving it. Then write has_one :user_profile, :inverse_of => :user in User, which lets the UserProfile know about the presence of its User object, even before either has been persisted and assigned an id.

Finally, you can include a before_create block in User to build the associated UserProfile when creating a new user. (I believe) it will run validations after building a new user_profile, so these should pass.

In summary,

class User < ActiveRecord::Base    has_one :user_profile, :inverse_of => :user    validates_presence_of :user_profile    before_create { build_user_profile }endclass UserProfile < ActiveRecord::Base  belongs_to :user  validates_presence_of :userend

UPDATE

I was mistaken about the validation-callback order. The validation runs before the before_create callback is called, which means User is checking for the presence of a UserProfile before one is even built.

One solution is to ask yourself what value you get from having separate user and user_profile models. Given that they are so tightly bound that one cannot exist without the other, would it make sense (and perhaps simplify a lot of your code) to just combine them into a single model?

On the other hand, if you really find that there is value in having two separate models, perhaps you shouldn't use validations to maintain their mutual existence. In my opinion, model validations should generally be used to let users know that the data they have submitted have errors they need to fix. However, the absence of a user_profile from their user object is not something they can fix. So, perhaps the better solution is to have the user object build a user_profile if there isn't one. Instead of just complaining if a user_profile doesn't exist, you take it a step further and just build it. No validation required on either side.

class User < ActiveRecord::Base  has_one :user_profile  before_save { build_user_profile unless user_profile }endclass UserProfile < ActiveRecord::Base  belongs_to :userend


You cannot validate the presence of user_profile_id because it does not exist. What has_one means is that the other model has a foreign key reference to it.

The way I generally ensure the behavior that you are after is by conditionally creating the model with the foreign key reference when the model being referenced is created. In your case that would be creating a profile after_create for the user like so:

class User < ActiveRecord::Base  ...  after_create :create_profile  private  def create_profile    self.user_profile.create  endend


This rail cast goes over making nested forms, (to create both user/user_profile together). http://railscasts.com/episodes/196-nested-model-form-part-1 there is some modifications you need to do since it covers a has_many but you should be able to figure it out.