views:

453

answers:

2

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.

+1  A: 
Kathy Van Stone
From what I had seen Rails will do some kind of quasi-transaction when you try to save a new object with new children. I obviously have to wrap everything in transactions if I'm manually saving each object along the way though
+1  A: 

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
Ethan
Do you have a constraint on the survey_id column of the survey_timepoint table? The table in our DB wasn't created with rails so that may be what's causing the funkiness.
Yes, I do have an FK constraint that I added manually outside of Rails.
Ethan
I apply FK constraints a lot in various Rails projects and haven't had any problems. Also, I have worked with legacy tables created outside Rails and it's fine. They just have to either follow the ActiveRecord naming conventions or you have to tell your models how they differ from the convention.
Ethan
Is yours properly creating the fk association between the entry in survey_timepoints and survey (survey_id = surve.id)? I've got my models set up in exactly the same way and it's still throwing that error. When I manually specify the foreign key in the relationship it will create the two but there's no association made. I must be missing something
Yes, it is creating the ID. You could try accessing the app in the console and creating some models there. Watch the SQL output for anything revealing. Try different ways of saving and see what errors result. I added another way to do the save in my answer.
Ethan