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
on 2006-05-28 16:39
on 2006-05-28 16:45
On May 28, 2006, at 3:37 pm, Nicholas Evans 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
on 2006-05-28 16:55
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
on 2006-05-28 17:37
Nicholas Evans 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.