validation prevents saving through associations #8277

Closed
sporto opened this Issue Nov 20, 2012 · 4 comments

3 participants

@sporto

Given a fresh rails app like with:

class Location < ActiveRecord::Base
  has_many :user_locations
  has_many :users, class_name: 'User', through: :user_locations
end

class User < ActiveRecord::Base
  attr_accessible :locations

  has_many :user_locations
  has_many :locations, through: :user_locations
end

class UserLocation < ActiveRecord::Base
  belongs_to :location
  belongs_to :user

  validates :location_id, presence: true
  validates :user_id, presence: true
end

When given a test:

loc1 = Location.create()
loc2 = Location.create()
user = User.create(locations: [loc1, loc2])
expect(user.errors).to be_empty
expect(user.locations.size).to eq(2)

It passes in rails 3.2.8
But fails in 3.2.9

In 3.2.9 the validations in UserLocation are preventing the same behaviour as in 3.2.8
validation error is "User locations is invalid, User locations is invalid"

@gregolsen

Same as #8238
Please, see discussion here #8269

@rafaelfranca
Ruby on Rails member

You should not add validation in the _id column.

This is what happen in 3.2.8

You try to create a User, the locations are set without create the join model in memory (this means your validations in join model don't run)

This is what happen in 3.2.9

You try to create a User, the locations are set creating the join model in memory (this means your validations run). The _id is not set, but the object is set.

I believe the behavior in 3.2.8 is buggy and the 3.2.9 is the right, but we need to discuss if we want to keep this behavior in a stable release.

I'm closing this one in favor of #8269

@sporto

Just to clarify

If I change the validation to:

validates :location, presence: true
validates :user, presence: true

test still fails, one way to make it pass is to remove the validation entirely.

Other way is using inverse_of (combined with validation on object, not id):

class User < ActiveRecord::Base
    attr_accessible :locations

    has_many :user_locations, inverse_of: :user
    has_many :locations, through: :user_locations
end

class UserLocation < ActiveRecord::Base
    belongs_to :location
    belongs_to :user

    validates :location, presence: true
    validates :user, presence: true
end
@rafaelfranca
Ruby on Rails member

Yes, you have to set :inverse_of if you want to use the object to validations

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment