Scott
Scott

Reputation:

Rails Trouble creating model instance with one to many relationship

I think there are a lot of places where my design may be screwing this up. I have very limited experience with Rails though. This is happening in Rails 2.3.2 with Postgres 8.3.

We've got two tables in our DB. One called "survey" and one called "survey_timepoint". A survey can have multiple time points so in the survey_timepoint table there is a column called "survey_id" with an fk constraint on it.

I also think I should mention that the tables were not created with a rails migration although they do follow the rails naming conventions. I suspect AR isn't anticipating a constraint on that column and it doesn't know how to handle the situation.

In my rails models I have:

has_many :survey_timepoint 

and

belongs_to :survey

If I do something like:

s = Survey.new
s.survey_timepoint.push SurveyTimepoint.new
s.save!

I get:

ActiveRecord::StatementInvalid: PGError: ERROR:  insert or update on table "survey_timepoints" violates foreign key constraint "survey_timepoints_fk"
DETAIL:  Key (survey_id)=(59) is not present in table "surveys"

I'm assuming that if I delete that fk constraint on survey_timepoint.survey_id it'll work ok. It seems like I shouldn't have too though. Am I going to be stuck creating and saving each of the objects separately and wrapping the whole process in a transaction? It seems rather un-railsy. Apologies for any necessary information that I may have omitted.

Upvotes: 2

Views: 1273

Answers (2)

Ethan
Ethan

Reputation: 60169

I just experimented and found that this works with MySQL:

s = Survey.new()
s.survey_timepoints << SurveyTimepoint.new  # Note "survey_timepoints" (plural)
s.save!

I think it would work equally well with PostgreSQL.

It does two inserts, first the Survey, then the timepoint, and wraps them in a transaction.

You can also do it all on one line:

Survey.create!({:name=>'New Survey', :survey_timepoints => [SurveyTimepoint.new]})

Incidentally, for ActiveRecord to work right you have to make sure of your singulars and plurals. (If you want to break the expected forms, you'll need to tell AR you're doing that -- a whole other topic.)

Your tables should be:

surveys
-------
# ...

survey_timepoints
-----------------
survey_id
# ...

And in your models you'd have:

class Survey < ActiveRecord::Base
  has_many :survey_timepoints
  # etc...
end

class SurveyTimepoint < ActiveRecord::Base
  belongs_to :survey
end

Upvotes: 1

Kathy Van Stone
Kathy Van Stone

Reputation: 26271

You might want to check the SQL commands being sent. It looks like it is adding the survey_timepoint record before the survey record. Note that you are already dealing with two database changes — the survey and the survey_timepoint — so you should be using a transaction.

You can fix the immediate problem by doing s.save! before adding the timepoint (and then calling it again). My knowledge of Rails functionality is not deep enough to know if there is a more "railsy" way of doing this then wrapping it in a transaction.

Upvotes: 1

Related Questions