12

Can anyone figure out what's going on here? I was able to get my code to work the way I want it to, but I can't figure out why validates_associated isn't working as I expect. Here's a snippet of my code:

class Flag < ActiveRecord::Base
  belongs_to :user
  belongs_to :post

  # allow only one flag per post per user
  validates_uniqueness_of :user_id, :scope => :post_id

  validates :user_id, :post_id, :presence => true
  validates_associated :user, :post

  attr_accessible :user_id, :post_id
end

With this code I can't save a flag with user_id == nil. I can save a flag with user_id == 12345 (i.e. some user_id not in the database). This is what the validates_associated API specification says:

validates_associated(*attr_names)

Validates whether the associated object or objects are all valid themselves. Works with any kind of association.
...
NOTE: This validation will not fail if the association hasn’t been assigned. If you want to ensure that the association is both present and guaranteed to be valid, you also need to use validates_presence_of.

I was able to get the desired behavior by using this, instead:

  validates :user, :post, :presence => true

My understanding of the API specification is that validates_associated checks the associated table to see if a row exists with an id matching the foreign key of Flag provided the foreign key is non-nil. Can anyone offer any insight on this? Am I misunderstanding how validates_associated is supposed to work?

Eric Hu
  • 18,048
  • 9
  • 51
  • 67
  • Yes..you are correct..that is what docs say. `validates_associated` means it validates any associated relation(if there is one) to exist in the other table. – rubyprince Mar 03 '11 at 04:14
  • thanks for the comment! That's what I interpreted it to mean, but it actually doesn't seem to do this--if you're curious, see my comments on KenB's answer, below. – Eric Hu Mar 03 '11 at 23:53
  • 2.5 years on, we're on Rails 4.0.0 and this still baffled me. As far as I'm concerned validates_associated doesn't do what it says it does and is completely confusing. – Jeremy Burton Sep 25 '13 at 00:23

2 Answers2

26

validates_associated simply runs the validations that are specified within the associated object's class, it does nothing in regard to foreign keys.

validates :user_id, :presence=>true ensures the presence of a user_id in your flag record, but that's all.

validates :user, :presence=>true is used on the association itself and ensures that foreign keys are properly set up.

seancdavis
  • 2,739
  • 2
  • 26
  • 36
KenB
  • 6,587
  • 2
  • 35
  • 31
  • So if I'm understanding this right, validates_associated would do flag.user.valid? but only if !user(flag.user_id).nil? Is this right? – Eric Hu Mar 03 '11 at 19:17
  • Right, if flag.user_id.nil?, then there is no associated object to run flag.user.valid? on, so validates_associated :user has nothing to do. – KenB Mar 03 '11 at 21:42
  • 1
    Thanks! that answers my question. This makes validates_associated seem somewhat useless to me. It would only complain when Flag.user.valid? == false, but that means that there is a user object in the Users table that's invalid (according to the User model validations). That only happens when I'm saving user objects to the database without running a validation check on them first--which seems like very bad programming. – Eric Hu Mar 03 '11 at 23:05
  • 1
    Just to clarify-- we could have flag.user_id.nil? == false in the following scenario: say, flag.user_id = 200 and User.find(:id => flag.user_id) == nil, then validates_associated would not complain i.e. flag.valid? == true. This is what prompted me to look into this, I made a syntax mistake with flag.new(:user_id => **some_user**) instead of flag.new(:user_id => **some_user.id**). This caused flag.user_id to be set to some numerical field in some_user and it wasn't the id field. Very confusing and it seems like validates :user, :presence => true does everything I want without the confusion – Eric Hu Mar 03 '11 at 23:13
  • 1
    It certainly sounds like validates :user, :presence=>true is what you need for your current context. validates_associated :model is more useful in situations where you have a parent model that accepts_nested_attributes_for :some_associated_model, and you are saving them both in one go. – KenB Mar 04 '11 at 02:16
  • Is `validates_presence_of :user` the same as `validates :user, presence: true`? – nc. Apr 03 '14 at 06:12
  • @KenB is right. `validates_associated` makes your `accepts_nested_attributes_for` validations better. Without it, child records only get validated if the child is modified. With it, they are always validated. The latter is what you want. – Chris Beck May 10 '15 at 16:25
0

Man... all I got was that validates_presence_of is needed for this to work as you got from the API. Seem's overkill to be checking for association validness, but I'm a noob.

thenengah
  • 42,557
  • 33
  • 113
  • 157
  • This is my first rails app, so it could be overkill. My reasoning is that I don't want to accidentally make objects like flags that are associated with nothing and allow it to save in the database – Eric Hu Mar 03 '11 at 19:24