Validating Foreign Key


#1

Howdy,

I’m a Rails beginner working on my first significant project with RoR.
I’m trying to figure out how to validate a foreign key (you know, make
sure the row actually exists.)

However, validates_associated doesn’t seem to be what I want to use, nor
does it work…

– BEGIN –
class City < ActiveRecord::Base
has_many :schools
belongs_to :state

validates_presence_of :city_name
validates_presence_of :state_id
validates_associated :state
end

class State < ActiveRecord::Base
has_many :cities

validates_presence_of :long_name
validates_presence_of :state_code
validates_length_of :state_code, :is => 2
validates_uniqueness_of :state_code
end
– END –

Regardless of all of that, my test can still create a city with a
state_id of 91, and I do not have a 91 row in my states table.

– From test.log –
State Load (0.000907) SELECT * FROM states WHERE (states.id = 91) LIMIT
1
SQL (0.000487) INSERT INTO cities (city_name, state_id)
VALUES(‘Hello!’, 91)
– END –

What am I looking to do here…? I googled, but there doesn’t seem to be
a really Rails-y way to do this. Do I need to write a custom
validation…?

Thank you!

  • Nick Evans

#2

On May 28, 2006, at 3:37 pm, Nicholas E. wrote:

I’m a Rails beginner working on my first significant project with
RoR. I’m trying to figure out how to validate a foreign key (you
know, make sure the row actually exists.)

Nicholas

You need to read the documentation for whatever database server you
are using. It will tell you how to make foreign key constraints so
that you can’t create rows in one table that reference non-existant
rows in another.

This is by far the best way to solve the problem as it is what
databases are made for.

Ashley


#3

Nicholas E. wrote the following on 28.05.2006 16:51 :

class City < ActiveRecord::Base
has_many :schools
belongs_to :state

validates_presence_of :city_name
validates_presence_of :state_id

What you want is:
validates_associated :state

validates_presence_of only tests if self.state_id != nil
(by the way it didn’t exactly do that in Rails 1.0.0, instead it tested
that self.state_id was true which bite me hard for booleans… which can
be false, didn’t test with 1.1.2 yet).

But pay attention to the fact that validates_associated probably isn’t
as robust as real foreign keys (you can delete an object referenced by
another without foreign keys, especially if you have concurrent accesses
which make application-level checks subject to race conditions).

Personally I use both foreign keys and validates_associated.
Foreign keys ensure data consistency, validates_associated in the models
make error handling easier (detecting error types by parsing DBMS error
messages is a pain…)

Note that for fixtures there is a workaround for the current limitations
by loading all inter-dependent fixtures in each test class in the
correct order.

Lionel.


#4

Ashley,

Rails doesn’t work well with foreign key constraints at the database
level (fixtures can get all messed up because it doesn’t know how to
load them in the correct order). Plus, I had always been told to never,
ever, evAr use key constraints and Rails…is that not true?

However, it looks like I’ve figured out my solution. I added a custom
validate method that tries to find the state.

class City < ActiveRecord::Base
has_many :schools
belongs_to :state

validates_presence_of :city_name
validates_presence_of :state_id

Validate is called before save

def validate
unless State.find_by_id(state_id)
errors.add(:state_id, ‘does not exist’)
end
end # validate
end

19 tests, 30 assertions, 0 failures, 0 errors (win!)

Thank you all the same!

  • Nick Evans