Given that the "Rails Way" seems to be not to use foreign key constraints, I'm looking for an alternative that will allow me to validate that the row a foreign key references does in fact exist in TableA before I save an object in TableB with table_a_id.
The only resource I've found to do this so far (can't find a link to the blog post it was mentioned in, was dated 2007) doesn't appear to be compatible with Rails 3.2, so can anyone suggest a way of doing this?
I'm currently looking at creating a validator to manually assign to the relevant attributes in my models, but I can't work out how to do it with validate_each(object, attribute, value).
There is a plugin that helps you with this for belongs_to associations: Validates existence of. But, maybe you can add your own validation? What about something like this:
# Assuming your foreign key is user_id (which references the table User)
validate :user_id_exists
def user_id_exists
return false if User.find_by_id(self.user_id).nil?
end
simply use like below,
validates :user, presence: true
It will automatically check the existence of user record in db.
I had problems with this piece of code:
return false if User.find(self.user_id).nil?
I had to catch the ActiveRecord exception when no matching record was found. nil? does not work when no record is found; the exception is thrown before nil? is executed.
# Assuming your foreign key is user_id (which references the table User)
validate :user_id_exists
def user_id_exists
begin
User.find(self.user_id)
rescue ActiveRecord::RecordNotFound
errors.add(:user_id, "user_id foreign key must exist")
false
end
end
This is useful when you use invalid? assertions in unit tests.
request.user_id = unknown
assert request.invalid?
request.user_id = 1
assert request.valid?
Note that as of Rails 3.2, the validates_presence_of works exactly the way you want it to in this case and you don't have to build a complicated structure like the above answers or even use the nice validates_existence_of gem.
I dislike exceptions. I solved this problem by doing this:
class Foo < ActiveRecord::Base
validate :bar_exists
protected
def bar_exists
if !User.where(bar_id: self.bar_id).exists? then
errors.add(:bar_id, 'A valid bar_id is valid.')
end
end
end
You need to specify the inverse_of
option and validate that it's presence is true.
From the Active Record Validations Guide:
In order to validate associated records whose presence is required,
you must specify the :inverse_of option for the association