validates_associated & foreign keys

Hi,

I'm struggling to get the validates_associated to work as I think it
should be.

I'm using:
JRuby 1.1
rails-2.0.2
activerecord-2.0.2
activerecord-jdbc-adapter-0.8.2

My tables in MySQL:
CREATE TABLE area_codes (
    id INT UNSIGNED auto_increment primary key
...
);

CREATE TABLE markets (
    id INT UNSIGNED auto_increment primary key,
...
);

CREATE TABLE area_codes_markets (
    id INT UNSIGNED auto_increment primary key,
    market_id INT UNSIGNED NOT NULL,
    area_code_id INT UNSIGNED NOT NULL,
    INDEX(market_id),
    INDEX(area_code_id),
    CONSTRAINT UNIQUE (market_id, area_code_id),
    CONSTRAINT FOREIGN KEY(market_id)
        REFERENCES markets(id) MATCH FULL ON DELETE RESTRICT,
    CONSTRAINT FOREIGN KEY(area_code_id)
        REFERENCES area_codes(id) MATCH FULL ON DELETE RESTRICT
)
ENGINE=InnoDB;

My model:
class AreaCodesMarket < ActiveRecord::Base
  has_many :area_codes
  has_many :markets
  validates_associated :area_codes
  validates_associated :markets

  validates_presence_of :area_code_id, :market_id
  validates_uniqueness_of :market_id, :scope => :area_code_id
end

When I create a new area_codes_market record and (deliberately) enter
an area_code_id or a market_id that is incorrect, Ruby on Rails
doesn't flag the error.
Instead I get an activerecord error on my foreign key constraint.

ActiveRecord::ActiveRecordError: Cannot add or update a child row: a
foreign key constraint fails
(`TestDoneRight_test`.`area_codes_markets`, CONSTRAINT
`area_codes_markets_ibfk_2` FOREIGN KEY (`area_code_id`) REFERENCES
`area_codes` (`id`)): INSERT INTO `area_codes_markets`
(`area_code_id`, `market_id`) VALUES(2, 1)

Reading the forums and the documentation, I think I have the correct
naming convention and model.

I found the following online
http://dev.rubyonrails.org/ticket/5369
However, the patch is for an older ActiveRecord version, so I've got
no idea how to apply it (assuming it would even work).

Does anyone have a clue how I could fix this?

Thanks, Birgit

When I create a new area_codes_market record and (deliberately) enter
an area_code_id or a market_id that is incorrect, Ruby on Rails
doesn't flag the error.
Instead I get an activerecord error on my foreign key constraint.

I think you've just set your hopes a little too high - rails doesn't
really do anything with foreign keys.
All validates_associated does is run the validations on the associated
models. If you were about to create a record with a dangling reference
then there's just nothing for validates_associated to validate.

On top of that i think you've got your associations wired up the wrong
way round. Given that you've got area_code_id and market_id columns
(and going by the name of your table) you should have
belongs_to :area_code and belongs_to :market not the has_manys you've
currently got (has_many area_codes implies that area_codes has a
area_codes_market_id column)

Fred

Hi Frederick,

Thanks for your message.
I changed my 'belongs_to' and 'has_many' and (indeed) the problem persists.

I know that RoR doesn't really know about foreign keys, but what is the
purpose of validates_associated ??
http://ar.rubyonrails.com/classes/ActiveRecord/Validations/ClassMethods.html#M000090

What kind of associating does it perform, it not on the values between
two models?

Maybe I should rephrase my question ...

If validates_associated doesn't do the trick, how do I write a
validation that checks that area_codes_market.area_code_id == area_codes.id

Thanks, Birgit

Hi Frederick,

Thanks for your message.
I changed my 'belongs_to' and 'has_many' and (indeed) the problem persists.

I know that RoR doesn't really know about foreign keys, but what is the
purpose of validates_associated ??
http://ar.rubyonrails.com/classes/ActiveRecord/Validations/ClassMethods.html#M000090

It runs the validations on the association. If there is no associated model (eg because the foreign key is dangling) it doesn't care

What kind of associating does it perform, it not on the values between
two models?

Maybe I should rephrase my question ...

If validates_associated doesn't do the trick, how do I write a
validation that checks that area_codes_market.area_code_id == area_codes.id

From the api docs:

NOTE: This validation will not fail if the association hasn't been assigned. If you want to ensure that the association is both present and guaranteed to be valid, you also need to use validates_presence_of.

Fred

The purpose of validates_associated is to know whether an object
associated with another object is valid (as in returns true when
calling the valid? method) This allows you, for example to check
whether an area code object associated to a market is valid before you
save both. if the associated object fails validation, then the parent
object cannot be saved either.

What Frederick is talking about (dangling references) is a check that
rails will not do for you on write (as in creating a row in the db)
but on read. Specifically on method calls to other objects.

So for instance i can create Product.new(:category_id => 2) where
product belongs to category. I save it no trouble, even change the
category_id later on

@product.category_id = 999
=> 999
@product.save
=> true

but then:

@product.category
=> nil # if Category.find_by_id(999) is nil

@product.category_id
=> 999
dunno if this helps, just thought it could shine some light for people
with similar problems

Thanks Wolas and Fred for you help and explanation.

I've decided to change the UI of market, so it will display checkboxes for the available area_codes. This way I avoid having the check for foreign keys, since the UI will only offer what's available.

For newbies (such as myself, specially when using Rails 2.0 and getting very confused when looking at Rails 1.X examples), the following really helped me out:

PaulBarry.com - has_many :through checkboxes
http://paulbarry.com/articles/2007/10/24/has_many-through-checkboxes

Cheers, Birgit

Excellent source!!

thx