Forum: Ruby on Rails Validating Foreign Key

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Nicholas E. (Guest)
on 2006-05-28 18:39
(Received via mailing list)
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
Ashley M. (Guest)
on 2006-05-28 18:45
(Received via mailing list)
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
Nicholas E. (Guest)
on 2006-05-28 18:55
(Received via mailing list)
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
Lionel B. (Guest)
on 2006-05-28 19:37
(Received via mailing list)
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.
This topic is locked and can not be replied to.