Rails - correct associations for pre-existing models












2















I am trying to create Rails models for some pre-existing database tables.



There is 1 MainTable, which has 1 ChildTable.



The problem could be the 'foreign keys' are named differently on each table



  class MainTable < ActiveRecord::Base
has_many :child_tables, :class_name => 'ChildTable', :foreign_key => "child_column_name"
accepts_nested_attributes_for :child_tables
self.primary_key = "main_table_column_name"
self.table_name = 'main_table'
end


class ChildTable < ActiveRecord::Base
belongs_to :main_table, :class_name => 'MainTable', foreign_key: "child_column_name", :primary_key => "main_table_column_name"
self.table_name = 'child_table'
self.primary_key = "child_table_column_name"

end


Given that the main table will only every have 1 record per child table, but there could be many child records per main table - do these associations look correct?



I want to be able to do something like:



m = MainTable.new
m.some_value = "123"
m.mntbl_key ="999"
c = ChildTable.new
c.something = "Foo"
m.child_tables << c

m.save!


EDIT: I am no longer getting an error as I have updated the code above. It works now BUT I have to assign the ids on both tables.
If I assign the id on the Master object, it will not automatically give the child object that id in the fk column
i.e.



m = MasterTable.new
m.master_table_column_name = 99
c = ChildTable.new
m.child_tables << c
m.save!

-- This will Insert ok into Master but will INSERT a blank row into the child


However:



m = MasterTable.new
m.master_table_column_name = 99
c = ChildTable.new
c.child_column_name = 99
m.child_tables << c
m.save!

--This works and inserts correctly into both tables


--------------------------------- OLD:
But I am getting the error:



ActiveModel::MissingAttributeError: can't write unknown attribute `ctbl_key'


Am I missing something?










share|improve this question

























  • Can you add migrations files for those tables?

    – Raoslaw Szamszur
    Nov 20 '18 at 16:21











  • No the tables preexist and I cannot control anything about them :(

    – user3437721
    Nov 20 '18 at 16:22






  • 1





    You don't need a foreign_key in the many side of a has_many relationship.

    – gd.silva
    Nov 20 '18 at 16:27











  • @user3437721 Yep of course. But what I've meant was not migrations but schema.rb file. Sorry brain fart.

    – Raoslaw Szamszur
    Nov 20 '18 at 16:35
















2















I am trying to create Rails models for some pre-existing database tables.



There is 1 MainTable, which has 1 ChildTable.



The problem could be the 'foreign keys' are named differently on each table



  class MainTable < ActiveRecord::Base
has_many :child_tables, :class_name => 'ChildTable', :foreign_key => "child_column_name"
accepts_nested_attributes_for :child_tables
self.primary_key = "main_table_column_name"
self.table_name = 'main_table'
end


class ChildTable < ActiveRecord::Base
belongs_to :main_table, :class_name => 'MainTable', foreign_key: "child_column_name", :primary_key => "main_table_column_name"
self.table_name = 'child_table'
self.primary_key = "child_table_column_name"

end


Given that the main table will only every have 1 record per child table, but there could be many child records per main table - do these associations look correct?



I want to be able to do something like:



m = MainTable.new
m.some_value = "123"
m.mntbl_key ="999"
c = ChildTable.new
c.something = "Foo"
m.child_tables << c

m.save!


EDIT: I am no longer getting an error as I have updated the code above. It works now BUT I have to assign the ids on both tables.
If I assign the id on the Master object, it will not automatically give the child object that id in the fk column
i.e.



m = MasterTable.new
m.master_table_column_name = 99
c = ChildTable.new
m.child_tables << c
m.save!

-- This will Insert ok into Master but will INSERT a blank row into the child


However:



m = MasterTable.new
m.master_table_column_name = 99
c = ChildTable.new
c.child_column_name = 99
m.child_tables << c
m.save!

--This works and inserts correctly into both tables


--------------------------------- OLD:
But I am getting the error:



ActiveModel::MissingAttributeError: can't write unknown attribute `ctbl_key'


Am I missing something?










share|improve this question

























  • Can you add migrations files for those tables?

    – Raoslaw Szamszur
    Nov 20 '18 at 16:21











  • No the tables preexist and I cannot control anything about them :(

    – user3437721
    Nov 20 '18 at 16:22






  • 1





    You don't need a foreign_key in the many side of a has_many relationship.

    – gd.silva
    Nov 20 '18 at 16:27











  • @user3437721 Yep of course. But what I've meant was not migrations but schema.rb file. Sorry brain fart.

    – Raoslaw Szamszur
    Nov 20 '18 at 16:35














2












2








2








I am trying to create Rails models for some pre-existing database tables.



There is 1 MainTable, which has 1 ChildTable.



The problem could be the 'foreign keys' are named differently on each table



  class MainTable < ActiveRecord::Base
has_many :child_tables, :class_name => 'ChildTable', :foreign_key => "child_column_name"
accepts_nested_attributes_for :child_tables
self.primary_key = "main_table_column_name"
self.table_name = 'main_table'
end


class ChildTable < ActiveRecord::Base
belongs_to :main_table, :class_name => 'MainTable', foreign_key: "child_column_name", :primary_key => "main_table_column_name"
self.table_name = 'child_table'
self.primary_key = "child_table_column_name"

end


Given that the main table will only every have 1 record per child table, but there could be many child records per main table - do these associations look correct?



I want to be able to do something like:



m = MainTable.new
m.some_value = "123"
m.mntbl_key ="999"
c = ChildTable.new
c.something = "Foo"
m.child_tables << c

m.save!


EDIT: I am no longer getting an error as I have updated the code above. It works now BUT I have to assign the ids on both tables.
If I assign the id on the Master object, it will not automatically give the child object that id in the fk column
i.e.



m = MasterTable.new
m.master_table_column_name = 99
c = ChildTable.new
m.child_tables << c
m.save!

-- This will Insert ok into Master but will INSERT a blank row into the child


However:



m = MasterTable.new
m.master_table_column_name = 99
c = ChildTable.new
c.child_column_name = 99
m.child_tables << c
m.save!

--This works and inserts correctly into both tables


--------------------------------- OLD:
But I am getting the error:



ActiveModel::MissingAttributeError: can't write unknown attribute `ctbl_key'


Am I missing something?










share|improve this question
















I am trying to create Rails models for some pre-existing database tables.



There is 1 MainTable, which has 1 ChildTable.



The problem could be the 'foreign keys' are named differently on each table



  class MainTable < ActiveRecord::Base
has_many :child_tables, :class_name => 'ChildTable', :foreign_key => "child_column_name"
accepts_nested_attributes_for :child_tables
self.primary_key = "main_table_column_name"
self.table_name = 'main_table'
end


class ChildTable < ActiveRecord::Base
belongs_to :main_table, :class_name => 'MainTable', foreign_key: "child_column_name", :primary_key => "main_table_column_name"
self.table_name = 'child_table'
self.primary_key = "child_table_column_name"

end


Given that the main table will only every have 1 record per child table, but there could be many child records per main table - do these associations look correct?



I want to be able to do something like:



m = MainTable.new
m.some_value = "123"
m.mntbl_key ="999"
c = ChildTable.new
c.something = "Foo"
m.child_tables << c

m.save!


EDIT: I am no longer getting an error as I have updated the code above. It works now BUT I have to assign the ids on both tables.
If I assign the id on the Master object, it will not automatically give the child object that id in the fk column
i.e.



m = MasterTable.new
m.master_table_column_name = 99
c = ChildTable.new
m.child_tables << c
m.save!

-- This will Insert ok into Master but will INSERT a blank row into the child


However:



m = MasterTable.new
m.master_table_column_name = 99
c = ChildTable.new
c.child_column_name = 99
m.child_tables << c
m.save!

--This works and inserts correctly into both tables


--------------------------------- OLD:
But I am getting the error:



ActiveModel::MissingAttributeError: can't write unknown attribute `ctbl_key'


Am I missing something?







ruby-on-rails






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 21 '18 at 9:45







user3437721

















asked Nov 20 '18 at 16:16









user3437721user3437721

88911532




88911532













  • Can you add migrations files for those tables?

    – Raoslaw Szamszur
    Nov 20 '18 at 16:21











  • No the tables preexist and I cannot control anything about them :(

    – user3437721
    Nov 20 '18 at 16:22






  • 1





    You don't need a foreign_key in the many side of a has_many relationship.

    – gd.silva
    Nov 20 '18 at 16:27











  • @user3437721 Yep of course. But what I've meant was not migrations but schema.rb file. Sorry brain fart.

    – Raoslaw Szamszur
    Nov 20 '18 at 16:35



















  • Can you add migrations files for those tables?

    – Raoslaw Szamszur
    Nov 20 '18 at 16:21











  • No the tables preexist and I cannot control anything about them :(

    – user3437721
    Nov 20 '18 at 16:22






  • 1





    You don't need a foreign_key in the many side of a has_many relationship.

    – gd.silva
    Nov 20 '18 at 16:27











  • @user3437721 Yep of course. But what I've meant was not migrations but schema.rb file. Sorry brain fart.

    – Raoslaw Szamszur
    Nov 20 '18 at 16:35

















Can you add migrations files for those tables?

– Raoslaw Szamszur
Nov 20 '18 at 16:21





Can you add migrations files for those tables?

– Raoslaw Szamszur
Nov 20 '18 at 16:21













No the tables preexist and I cannot control anything about them :(

– user3437721
Nov 20 '18 at 16:22





No the tables preexist and I cannot control anything about them :(

– user3437721
Nov 20 '18 at 16:22




1




1





You don't need a foreign_key in the many side of a has_many relationship.

– gd.silva
Nov 20 '18 at 16:27





You don't need a foreign_key in the many side of a has_many relationship.

– gd.silva
Nov 20 '18 at 16:27













@user3437721 Yep of course. But what I've meant was not migrations but schema.rb file. Sorry brain fart.

– Raoslaw Szamszur
Nov 20 '18 at 16:35





@user3437721 Yep of course. But what I've meant was not migrations but schema.rb file. Sorry brain fart.

– Raoslaw Szamszur
Nov 20 '18 at 16:35












2 Answers
2






active

oldest

votes


















1














A one-to-many association only uses one foreign key column - not two.



Typically in rails its setup as so:



class Parent 
has_many :children
end

class Child
belongs_to :parent
end


The actual association is stored in the children.parent_id column.



So lets say we have an non-conventional foreign key:



class Parent 
has_many :children, foreign_key: 'padre_id'
end

class Child
belongs_to :parent, foreign_key: 'padre_id'
end


Easy enough. We just have to tell the associations on both side what the foreign key is. Note that the class_name option is not needed as long as the class name can be deduced from the name of the association.



Custom primary keys or table names are not a problem either as ActiveRecord looks at the model class definitions when resolving associations.



class Parent 
self.primary_key = :custom_pk
has_many :children, foreign_key: 'padre_id'
end

class Child
self.table_name = 'bar'
# this will correctly reference parents.foo
belongs_to :parent, foreign_key: 'padre_id'
end


There is also the universal issue that you must save a record before you can add children to it:



irb(main):024:0> m = MainTable.new
=> #<MainTable custom_pk: nil, created_at: nil, updated_at: nil>
irb(main):025:0> m.child_tables.new
=> #<ChildTable id: nil, padre_id: nil, created_at: nil, updated_at: nil>
irb(main):026:0> m.save!
(0.3ms) BEGIN
(0.4ms) ROLLBACK
ActiveRecord::RecordInvalid: Validation failed: Child tables is invalid
from (irb):26


Starting in Rails 5 belongs_to associations are non-optional by default. So the child_tables instance is not valid since the padre_id is nil.



You either need to save the parent record first:



irb(main):033:0> m = MainTable.create!
(0.5ms) BEGIN
MainTable Create (0.7ms) INSERT INTO "main_table" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "custom_pk" [["created_at", "2018-11-20 17:31:09.545476"], ["updated_at", "2018-11-20 17:31:09.545476"]]
(0.8ms) COMMIT
=> #<MainTable custom_pk: 5, created_at: "2018-11-20 17:31:09", updated_at: "2018-11-20 17:31:09">
irb(main):034:0> m.child_tables.new
=> #<ChildTable id: nil, padre_id: 5, created_at: nil, updated_at: nil>
irb(main):035:0> m.save
(0.3ms) BEGIN
MainTable Load (0.6ms) SELECT "main_table".* FROM "main_table" WHERE "main_table"."custom_pk" = $1 LIMIT $2 [["custom_pk", 5], ["LIMIT", 1]]
ChildTable Create (0.9ms) INSERT INTO "child_table" ("padre_id", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["padre_id", 5], ["created_at", "2018-11-20 17:31:21.737989"], ["updated_at", "2018-11-20 17:31:21.737989"]]
(0.6ms) COMMIT
=> true
irb(main):036:0>


Or build from the belongs_to association on the other side:



irb(main):027:0> c = ChildTable.new
=> #<ChildTable id: nil, padre_id: nil, created_at: nil, updated_at: nil>
irb(main):028:0> c.build_main_table
=> #<MainTable custom_pk: nil, created_at: nil, updated_at: nil>
irb(main):029:0> c.save!
(0.3ms) BEGIN
MainTable Create (0.8ms) INSERT INTO "main_table" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "custom_pk" [["created_at", "2018-11-20 17:24:29.344332"], ["updated_at", "2018-11-20 17:24:29.344332"]]
ChildTable Create (1.1ms) INSERT INTO "child_table" ("padre_id", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["padre_id", 3], ["created_at", "2018-11-20 17:24:29.346764"], ["updated_at", "2018-11-20 17:24:29.346764"]]
(0.7ms) COMMIT
=> true





share|improve this answer


























  • but my foreign keys are named different on each table, I'm not sure what your saying here?

    – user3437721
    Nov 20 '18 at 17:40











  • Are you saying it can't be done with a foreign key called something different in each table?

    – user3437721
    Nov 20 '18 at 18:02











  • No, I'm saying that a one to many association is built with only one foreign key (which is on the belongs_to side). The foreign key references the primary key on the other table. Since its one column on one table it cannot have different names on each side. This is really basic relational database design stuff and applies everywhere. Not just rails.

    – max
    Nov 20 '18 at 18:57













  • Thanks I'll try it out and update tomorrow, appreciate the response.

    – user3437721
    Nov 20 '18 at 19:07











  • Could you maybe update the answer so it is clear which column I should reference as the foreign key, in your example you use 'padre_id' which I assume is the parent column? Remember it is not the primary key of the parent table which is stored in the child table.

    – user3437721
    Nov 20 '18 at 19:23



















0














Not sure if it's the issue you're facing, but at the moment you're initializing new MainTables and ChildTables, however you're not saving them.



This means they won't be assigned an ID, which in turn means when you try and assign a Child Table to the Main Table, it doesn't have an ID for the main table (or itself) to create a join record with.



Try saving the records before shovelling in child records






share|improve this answer
























  • the idea is to have a cascading save so they all get saved at the same time..

    – user3437721
    Nov 20 '18 at 16:42











  • I'm not sure it's possible to assign a child record before the parent record is saved - it will try to create a join record which requires both the parent ID and child ID. I'm pretty sure the minimum is that the parent record is saved first

    – Mark
    Nov 20 '18 at 16:45











  • If you really want to go down that route I think you'll need to look at api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/…, specifically 'accepts_nested_attributes_for', which allows you to create a parent and child record in one transaction

    – Mark
    Nov 20 '18 at 16:46











  • In your case this would let you do: MainTable.create(title: 'bla', child_table_attributes: { title: 'bla2' } ) Which will create a Main Table, with a child table related to it

    – Mark
    Nov 20 '18 at 16:47











Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53397174%2frails-correct-associations-for-pre-existing-models%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























2 Answers
2






active

oldest

votes








2 Answers
2






active

oldest

votes









active

oldest

votes






active

oldest

votes









1














A one-to-many association only uses one foreign key column - not two.



Typically in rails its setup as so:



class Parent 
has_many :children
end

class Child
belongs_to :parent
end


The actual association is stored in the children.parent_id column.



So lets say we have an non-conventional foreign key:



class Parent 
has_many :children, foreign_key: 'padre_id'
end

class Child
belongs_to :parent, foreign_key: 'padre_id'
end


Easy enough. We just have to tell the associations on both side what the foreign key is. Note that the class_name option is not needed as long as the class name can be deduced from the name of the association.



Custom primary keys or table names are not a problem either as ActiveRecord looks at the model class definitions when resolving associations.



class Parent 
self.primary_key = :custom_pk
has_many :children, foreign_key: 'padre_id'
end

class Child
self.table_name = 'bar'
# this will correctly reference parents.foo
belongs_to :parent, foreign_key: 'padre_id'
end


There is also the universal issue that you must save a record before you can add children to it:



irb(main):024:0> m = MainTable.new
=> #<MainTable custom_pk: nil, created_at: nil, updated_at: nil>
irb(main):025:0> m.child_tables.new
=> #<ChildTable id: nil, padre_id: nil, created_at: nil, updated_at: nil>
irb(main):026:0> m.save!
(0.3ms) BEGIN
(0.4ms) ROLLBACK
ActiveRecord::RecordInvalid: Validation failed: Child tables is invalid
from (irb):26


Starting in Rails 5 belongs_to associations are non-optional by default. So the child_tables instance is not valid since the padre_id is nil.



You either need to save the parent record first:



irb(main):033:0> m = MainTable.create!
(0.5ms) BEGIN
MainTable Create (0.7ms) INSERT INTO "main_table" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "custom_pk" [["created_at", "2018-11-20 17:31:09.545476"], ["updated_at", "2018-11-20 17:31:09.545476"]]
(0.8ms) COMMIT
=> #<MainTable custom_pk: 5, created_at: "2018-11-20 17:31:09", updated_at: "2018-11-20 17:31:09">
irb(main):034:0> m.child_tables.new
=> #<ChildTable id: nil, padre_id: 5, created_at: nil, updated_at: nil>
irb(main):035:0> m.save
(0.3ms) BEGIN
MainTable Load (0.6ms) SELECT "main_table".* FROM "main_table" WHERE "main_table"."custom_pk" = $1 LIMIT $2 [["custom_pk", 5], ["LIMIT", 1]]
ChildTable Create (0.9ms) INSERT INTO "child_table" ("padre_id", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["padre_id", 5], ["created_at", "2018-11-20 17:31:21.737989"], ["updated_at", "2018-11-20 17:31:21.737989"]]
(0.6ms) COMMIT
=> true
irb(main):036:0>


Or build from the belongs_to association on the other side:



irb(main):027:0> c = ChildTable.new
=> #<ChildTable id: nil, padre_id: nil, created_at: nil, updated_at: nil>
irb(main):028:0> c.build_main_table
=> #<MainTable custom_pk: nil, created_at: nil, updated_at: nil>
irb(main):029:0> c.save!
(0.3ms) BEGIN
MainTable Create (0.8ms) INSERT INTO "main_table" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "custom_pk" [["created_at", "2018-11-20 17:24:29.344332"], ["updated_at", "2018-11-20 17:24:29.344332"]]
ChildTable Create (1.1ms) INSERT INTO "child_table" ("padre_id", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["padre_id", 3], ["created_at", "2018-11-20 17:24:29.346764"], ["updated_at", "2018-11-20 17:24:29.346764"]]
(0.7ms) COMMIT
=> true





share|improve this answer


























  • but my foreign keys are named different on each table, I'm not sure what your saying here?

    – user3437721
    Nov 20 '18 at 17:40











  • Are you saying it can't be done with a foreign key called something different in each table?

    – user3437721
    Nov 20 '18 at 18:02











  • No, I'm saying that a one to many association is built with only one foreign key (which is on the belongs_to side). The foreign key references the primary key on the other table. Since its one column on one table it cannot have different names on each side. This is really basic relational database design stuff and applies everywhere. Not just rails.

    – max
    Nov 20 '18 at 18:57













  • Thanks I'll try it out and update tomorrow, appreciate the response.

    – user3437721
    Nov 20 '18 at 19:07











  • Could you maybe update the answer so it is clear which column I should reference as the foreign key, in your example you use 'padre_id' which I assume is the parent column? Remember it is not the primary key of the parent table which is stored in the child table.

    – user3437721
    Nov 20 '18 at 19:23
















1














A one-to-many association only uses one foreign key column - not two.



Typically in rails its setup as so:



class Parent 
has_many :children
end

class Child
belongs_to :parent
end


The actual association is stored in the children.parent_id column.



So lets say we have an non-conventional foreign key:



class Parent 
has_many :children, foreign_key: 'padre_id'
end

class Child
belongs_to :parent, foreign_key: 'padre_id'
end


Easy enough. We just have to tell the associations on both side what the foreign key is. Note that the class_name option is not needed as long as the class name can be deduced from the name of the association.



Custom primary keys or table names are not a problem either as ActiveRecord looks at the model class definitions when resolving associations.



class Parent 
self.primary_key = :custom_pk
has_many :children, foreign_key: 'padre_id'
end

class Child
self.table_name = 'bar'
# this will correctly reference parents.foo
belongs_to :parent, foreign_key: 'padre_id'
end


There is also the universal issue that you must save a record before you can add children to it:



irb(main):024:0> m = MainTable.new
=> #<MainTable custom_pk: nil, created_at: nil, updated_at: nil>
irb(main):025:0> m.child_tables.new
=> #<ChildTable id: nil, padre_id: nil, created_at: nil, updated_at: nil>
irb(main):026:0> m.save!
(0.3ms) BEGIN
(0.4ms) ROLLBACK
ActiveRecord::RecordInvalid: Validation failed: Child tables is invalid
from (irb):26


Starting in Rails 5 belongs_to associations are non-optional by default. So the child_tables instance is not valid since the padre_id is nil.



You either need to save the parent record first:



irb(main):033:0> m = MainTable.create!
(0.5ms) BEGIN
MainTable Create (0.7ms) INSERT INTO "main_table" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "custom_pk" [["created_at", "2018-11-20 17:31:09.545476"], ["updated_at", "2018-11-20 17:31:09.545476"]]
(0.8ms) COMMIT
=> #<MainTable custom_pk: 5, created_at: "2018-11-20 17:31:09", updated_at: "2018-11-20 17:31:09">
irb(main):034:0> m.child_tables.new
=> #<ChildTable id: nil, padre_id: 5, created_at: nil, updated_at: nil>
irb(main):035:0> m.save
(0.3ms) BEGIN
MainTable Load (0.6ms) SELECT "main_table".* FROM "main_table" WHERE "main_table"."custom_pk" = $1 LIMIT $2 [["custom_pk", 5], ["LIMIT", 1]]
ChildTable Create (0.9ms) INSERT INTO "child_table" ("padre_id", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["padre_id", 5], ["created_at", "2018-11-20 17:31:21.737989"], ["updated_at", "2018-11-20 17:31:21.737989"]]
(0.6ms) COMMIT
=> true
irb(main):036:0>


Or build from the belongs_to association on the other side:



irb(main):027:0> c = ChildTable.new
=> #<ChildTable id: nil, padre_id: nil, created_at: nil, updated_at: nil>
irb(main):028:0> c.build_main_table
=> #<MainTable custom_pk: nil, created_at: nil, updated_at: nil>
irb(main):029:0> c.save!
(0.3ms) BEGIN
MainTable Create (0.8ms) INSERT INTO "main_table" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "custom_pk" [["created_at", "2018-11-20 17:24:29.344332"], ["updated_at", "2018-11-20 17:24:29.344332"]]
ChildTable Create (1.1ms) INSERT INTO "child_table" ("padre_id", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["padre_id", 3], ["created_at", "2018-11-20 17:24:29.346764"], ["updated_at", "2018-11-20 17:24:29.346764"]]
(0.7ms) COMMIT
=> true





share|improve this answer


























  • but my foreign keys are named different on each table, I'm not sure what your saying here?

    – user3437721
    Nov 20 '18 at 17:40











  • Are you saying it can't be done with a foreign key called something different in each table?

    – user3437721
    Nov 20 '18 at 18:02











  • No, I'm saying that a one to many association is built with only one foreign key (which is on the belongs_to side). The foreign key references the primary key on the other table. Since its one column on one table it cannot have different names on each side. This is really basic relational database design stuff and applies everywhere. Not just rails.

    – max
    Nov 20 '18 at 18:57













  • Thanks I'll try it out and update tomorrow, appreciate the response.

    – user3437721
    Nov 20 '18 at 19:07











  • Could you maybe update the answer so it is clear which column I should reference as the foreign key, in your example you use 'padre_id' which I assume is the parent column? Remember it is not the primary key of the parent table which is stored in the child table.

    – user3437721
    Nov 20 '18 at 19:23














1












1








1







A one-to-many association only uses one foreign key column - not two.



Typically in rails its setup as so:



class Parent 
has_many :children
end

class Child
belongs_to :parent
end


The actual association is stored in the children.parent_id column.



So lets say we have an non-conventional foreign key:



class Parent 
has_many :children, foreign_key: 'padre_id'
end

class Child
belongs_to :parent, foreign_key: 'padre_id'
end


Easy enough. We just have to tell the associations on both side what the foreign key is. Note that the class_name option is not needed as long as the class name can be deduced from the name of the association.



Custom primary keys or table names are not a problem either as ActiveRecord looks at the model class definitions when resolving associations.



class Parent 
self.primary_key = :custom_pk
has_many :children, foreign_key: 'padre_id'
end

class Child
self.table_name = 'bar'
# this will correctly reference parents.foo
belongs_to :parent, foreign_key: 'padre_id'
end


There is also the universal issue that you must save a record before you can add children to it:



irb(main):024:0> m = MainTable.new
=> #<MainTable custom_pk: nil, created_at: nil, updated_at: nil>
irb(main):025:0> m.child_tables.new
=> #<ChildTable id: nil, padre_id: nil, created_at: nil, updated_at: nil>
irb(main):026:0> m.save!
(0.3ms) BEGIN
(0.4ms) ROLLBACK
ActiveRecord::RecordInvalid: Validation failed: Child tables is invalid
from (irb):26


Starting in Rails 5 belongs_to associations are non-optional by default. So the child_tables instance is not valid since the padre_id is nil.



You either need to save the parent record first:



irb(main):033:0> m = MainTable.create!
(0.5ms) BEGIN
MainTable Create (0.7ms) INSERT INTO "main_table" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "custom_pk" [["created_at", "2018-11-20 17:31:09.545476"], ["updated_at", "2018-11-20 17:31:09.545476"]]
(0.8ms) COMMIT
=> #<MainTable custom_pk: 5, created_at: "2018-11-20 17:31:09", updated_at: "2018-11-20 17:31:09">
irb(main):034:0> m.child_tables.new
=> #<ChildTable id: nil, padre_id: 5, created_at: nil, updated_at: nil>
irb(main):035:0> m.save
(0.3ms) BEGIN
MainTable Load (0.6ms) SELECT "main_table".* FROM "main_table" WHERE "main_table"."custom_pk" = $1 LIMIT $2 [["custom_pk", 5], ["LIMIT", 1]]
ChildTable Create (0.9ms) INSERT INTO "child_table" ("padre_id", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["padre_id", 5], ["created_at", "2018-11-20 17:31:21.737989"], ["updated_at", "2018-11-20 17:31:21.737989"]]
(0.6ms) COMMIT
=> true
irb(main):036:0>


Or build from the belongs_to association on the other side:



irb(main):027:0> c = ChildTable.new
=> #<ChildTable id: nil, padre_id: nil, created_at: nil, updated_at: nil>
irb(main):028:0> c.build_main_table
=> #<MainTable custom_pk: nil, created_at: nil, updated_at: nil>
irb(main):029:0> c.save!
(0.3ms) BEGIN
MainTable Create (0.8ms) INSERT INTO "main_table" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "custom_pk" [["created_at", "2018-11-20 17:24:29.344332"], ["updated_at", "2018-11-20 17:24:29.344332"]]
ChildTable Create (1.1ms) INSERT INTO "child_table" ("padre_id", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["padre_id", 3], ["created_at", "2018-11-20 17:24:29.346764"], ["updated_at", "2018-11-20 17:24:29.346764"]]
(0.7ms) COMMIT
=> true





share|improve this answer















A one-to-many association only uses one foreign key column - not two.



Typically in rails its setup as so:



class Parent 
has_many :children
end

class Child
belongs_to :parent
end


The actual association is stored in the children.parent_id column.



So lets say we have an non-conventional foreign key:



class Parent 
has_many :children, foreign_key: 'padre_id'
end

class Child
belongs_to :parent, foreign_key: 'padre_id'
end


Easy enough. We just have to tell the associations on both side what the foreign key is. Note that the class_name option is not needed as long as the class name can be deduced from the name of the association.



Custom primary keys or table names are not a problem either as ActiveRecord looks at the model class definitions when resolving associations.



class Parent 
self.primary_key = :custom_pk
has_many :children, foreign_key: 'padre_id'
end

class Child
self.table_name = 'bar'
# this will correctly reference parents.foo
belongs_to :parent, foreign_key: 'padre_id'
end


There is also the universal issue that you must save a record before you can add children to it:



irb(main):024:0> m = MainTable.new
=> #<MainTable custom_pk: nil, created_at: nil, updated_at: nil>
irb(main):025:0> m.child_tables.new
=> #<ChildTable id: nil, padre_id: nil, created_at: nil, updated_at: nil>
irb(main):026:0> m.save!
(0.3ms) BEGIN
(0.4ms) ROLLBACK
ActiveRecord::RecordInvalid: Validation failed: Child tables is invalid
from (irb):26


Starting in Rails 5 belongs_to associations are non-optional by default. So the child_tables instance is not valid since the padre_id is nil.



You either need to save the parent record first:



irb(main):033:0> m = MainTable.create!
(0.5ms) BEGIN
MainTable Create (0.7ms) INSERT INTO "main_table" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "custom_pk" [["created_at", "2018-11-20 17:31:09.545476"], ["updated_at", "2018-11-20 17:31:09.545476"]]
(0.8ms) COMMIT
=> #<MainTable custom_pk: 5, created_at: "2018-11-20 17:31:09", updated_at: "2018-11-20 17:31:09">
irb(main):034:0> m.child_tables.new
=> #<ChildTable id: nil, padre_id: 5, created_at: nil, updated_at: nil>
irb(main):035:0> m.save
(0.3ms) BEGIN
MainTable Load (0.6ms) SELECT "main_table".* FROM "main_table" WHERE "main_table"."custom_pk" = $1 LIMIT $2 [["custom_pk", 5], ["LIMIT", 1]]
ChildTable Create (0.9ms) INSERT INTO "child_table" ("padre_id", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["padre_id", 5], ["created_at", "2018-11-20 17:31:21.737989"], ["updated_at", "2018-11-20 17:31:21.737989"]]
(0.6ms) COMMIT
=> true
irb(main):036:0>


Or build from the belongs_to association on the other side:



irb(main):027:0> c = ChildTable.new
=> #<ChildTable id: nil, padre_id: nil, created_at: nil, updated_at: nil>
irb(main):028:0> c.build_main_table
=> #<MainTable custom_pk: nil, created_at: nil, updated_at: nil>
irb(main):029:0> c.save!
(0.3ms) BEGIN
MainTable Create (0.8ms) INSERT INTO "main_table" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "custom_pk" [["created_at", "2018-11-20 17:24:29.344332"], ["updated_at", "2018-11-20 17:24:29.344332"]]
ChildTable Create (1.1ms) INSERT INTO "child_table" ("padre_id", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["padre_id", 3], ["created_at", "2018-11-20 17:24:29.346764"], ["updated_at", "2018-11-20 17:24:29.346764"]]
(0.7ms) COMMIT
=> true






share|improve this answer














share|improve this answer



share|improve this answer








edited Nov 20 '18 at 17:32

























answered Nov 20 '18 at 17:09









maxmax

46.2k1060104




46.2k1060104













  • but my foreign keys are named different on each table, I'm not sure what your saying here?

    – user3437721
    Nov 20 '18 at 17:40











  • Are you saying it can't be done with a foreign key called something different in each table?

    – user3437721
    Nov 20 '18 at 18:02











  • No, I'm saying that a one to many association is built with only one foreign key (which is on the belongs_to side). The foreign key references the primary key on the other table. Since its one column on one table it cannot have different names on each side. This is really basic relational database design stuff and applies everywhere. Not just rails.

    – max
    Nov 20 '18 at 18:57













  • Thanks I'll try it out and update tomorrow, appreciate the response.

    – user3437721
    Nov 20 '18 at 19:07











  • Could you maybe update the answer so it is clear which column I should reference as the foreign key, in your example you use 'padre_id' which I assume is the parent column? Remember it is not the primary key of the parent table which is stored in the child table.

    – user3437721
    Nov 20 '18 at 19:23



















  • but my foreign keys are named different on each table, I'm not sure what your saying here?

    – user3437721
    Nov 20 '18 at 17:40











  • Are you saying it can't be done with a foreign key called something different in each table?

    – user3437721
    Nov 20 '18 at 18:02











  • No, I'm saying that a one to many association is built with only one foreign key (which is on the belongs_to side). The foreign key references the primary key on the other table. Since its one column on one table it cannot have different names on each side. This is really basic relational database design stuff and applies everywhere. Not just rails.

    – max
    Nov 20 '18 at 18:57













  • Thanks I'll try it out and update tomorrow, appreciate the response.

    – user3437721
    Nov 20 '18 at 19:07











  • Could you maybe update the answer so it is clear which column I should reference as the foreign key, in your example you use 'padre_id' which I assume is the parent column? Remember it is not the primary key of the parent table which is stored in the child table.

    – user3437721
    Nov 20 '18 at 19:23

















but my foreign keys are named different on each table, I'm not sure what your saying here?

– user3437721
Nov 20 '18 at 17:40





but my foreign keys are named different on each table, I'm not sure what your saying here?

– user3437721
Nov 20 '18 at 17:40













Are you saying it can't be done with a foreign key called something different in each table?

– user3437721
Nov 20 '18 at 18:02





Are you saying it can't be done with a foreign key called something different in each table?

– user3437721
Nov 20 '18 at 18:02













No, I'm saying that a one to many association is built with only one foreign key (which is on the belongs_to side). The foreign key references the primary key on the other table. Since its one column on one table it cannot have different names on each side. This is really basic relational database design stuff and applies everywhere. Not just rails.

– max
Nov 20 '18 at 18:57







No, I'm saying that a one to many association is built with only one foreign key (which is on the belongs_to side). The foreign key references the primary key on the other table. Since its one column on one table it cannot have different names on each side. This is really basic relational database design stuff and applies everywhere. Not just rails.

– max
Nov 20 '18 at 18:57















Thanks I'll try it out and update tomorrow, appreciate the response.

– user3437721
Nov 20 '18 at 19:07





Thanks I'll try it out and update tomorrow, appreciate the response.

– user3437721
Nov 20 '18 at 19:07













Could you maybe update the answer so it is clear which column I should reference as the foreign key, in your example you use 'padre_id' which I assume is the parent column? Remember it is not the primary key of the parent table which is stored in the child table.

– user3437721
Nov 20 '18 at 19:23





Could you maybe update the answer so it is clear which column I should reference as the foreign key, in your example you use 'padre_id' which I assume is the parent column? Remember it is not the primary key of the parent table which is stored in the child table.

– user3437721
Nov 20 '18 at 19:23













0














Not sure if it's the issue you're facing, but at the moment you're initializing new MainTables and ChildTables, however you're not saving them.



This means they won't be assigned an ID, which in turn means when you try and assign a Child Table to the Main Table, it doesn't have an ID for the main table (or itself) to create a join record with.



Try saving the records before shovelling in child records






share|improve this answer
























  • the idea is to have a cascading save so they all get saved at the same time..

    – user3437721
    Nov 20 '18 at 16:42











  • I'm not sure it's possible to assign a child record before the parent record is saved - it will try to create a join record which requires both the parent ID and child ID. I'm pretty sure the minimum is that the parent record is saved first

    – Mark
    Nov 20 '18 at 16:45











  • If you really want to go down that route I think you'll need to look at api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/…, specifically 'accepts_nested_attributes_for', which allows you to create a parent and child record in one transaction

    – Mark
    Nov 20 '18 at 16:46











  • In your case this would let you do: MainTable.create(title: 'bla', child_table_attributes: { title: 'bla2' } ) Which will create a Main Table, with a child table related to it

    – Mark
    Nov 20 '18 at 16:47
















0














Not sure if it's the issue you're facing, but at the moment you're initializing new MainTables and ChildTables, however you're not saving them.



This means they won't be assigned an ID, which in turn means when you try and assign a Child Table to the Main Table, it doesn't have an ID for the main table (or itself) to create a join record with.



Try saving the records before shovelling in child records






share|improve this answer
























  • the idea is to have a cascading save so they all get saved at the same time..

    – user3437721
    Nov 20 '18 at 16:42











  • I'm not sure it's possible to assign a child record before the parent record is saved - it will try to create a join record which requires both the parent ID and child ID. I'm pretty sure the minimum is that the parent record is saved first

    – Mark
    Nov 20 '18 at 16:45











  • If you really want to go down that route I think you'll need to look at api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/…, specifically 'accepts_nested_attributes_for', which allows you to create a parent and child record in one transaction

    – Mark
    Nov 20 '18 at 16:46











  • In your case this would let you do: MainTable.create(title: 'bla', child_table_attributes: { title: 'bla2' } ) Which will create a Main Table, with a child table related to it

    – Mark
    Nov 20 '18 at 16:47














0












0








0







Not sure if it's the issue you're facing, but at the moment you're initializing new MainTables and ChildTables, however you're not saving them.



This means they won't be assigned an ID, which in turn means when you try and assign a Child Table to the Main Table, it doesn't have an ID for the main table (or itself) to create a join record with.



Try saving the records before shovelling in child records






share|improve this answer













Not sure if it's the issue you're facing, but at the moment you're initializing new MainTables and ChildTables, however you're not saving them.



This means they won't be assigned an ID, which in turn means when you try and assign a Child Table to the Main Table, it doesn't have an ID for the main table (or itself) to create a join record with.



Try saving the records before shovelling in child records







share|improve this answer












share|improve this answer



share|improve this answer










answered Nov 20 '18 at 16:38









MarkMark

1,9961724




1,9961724













  • the idea is to have a cascading save so they all get saved at the same time..

    – user3437721
    Nov 20 '18 at 16:42











  • I'm not sure it's possible to assign a child record before the parent record is saved - it will try to create a join record which requires both the parent ID and child ID. I'm pretty sure the minimum is that the parent record is saved first

    – Mark
    Nov 20 '18 at 16:45











  • If you really want to go down that route I think you'll need to look at api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/…, specifically 'accepts_nested_attributes_for', which allows you to create a parent and child record in one transaction

    – Mark
    Nov 20 '18 at 16:46











  • In your case this would let you do: MainTable.create(title: 'bla', child_table_attributes: { title: 'bla2' } ) Which will create a Main Table, with a child table related to it

    – Mark
    Nov 20 '18 at 16:47



















  • the idea is to have a cascading save so they all get saved at the same time..

    – user3437721
    Nov 20 '18 at 16:42











  • I'm not sure it's possible to assign a child record before the parent record is saved - it will try to create a join record which requires both the parent ID and child ID. I'm pretty sure the minimum is that the parent record is saved first

    – Mark
    Nov 20 '18 at 16:45











  • If you really want to go down that route I think you'll need to look at api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/…, specifically 'accepts_nested_attributes_for', which allows you to create a parent and child record in one transaction

    – Mark
    Nov 20 '18 at 16:46











  • In your case this would let you do: MainTable.create(title: 'bla', child_table_attributes: { title: 'bla2' } ) Which will create a Main Table, with a child table related to it

    – Mark
    Nov 20 '18 at 16:47

















the idea is to have a cascading save so they all get saved at the same time..

– user3437721
Nov 20 '18 at 16:42





the idea is to have a cascading save so they all get saved at the same time..

– user3437721
Nov 20 '18 at 16:42













I'm not sure it's possible to assign a child record before the parent record is saved - it will try to create a join record which requires both the parent ID and child ID. I'm pretty sure the minimum is that the parent record is saved first

– Mark
Nov 20 '18 at 16:45





I'm not sure it's possible to assign a child record before the parent record is saved - it will try to create a join record which requires both the parent ID and child ID. I'm pretty sure the minimum is that the parent record is saved first

– Mark
Nov 20 '18 at 16:45













If you really want to go down that route I think you'll need to look at api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/…, specifically 'accepts_nested_attributes_for', which allows you to create a parent and child record in one transaction

– Mark
Nov 20 '18 at 16:46





If you really want to go down that route I think you'll need to look at api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/…, specifically 'accepts_nested_attributes_for', which allows you to create a parent and child record in one transaction

– Mark
Nov 20 '18 at 16:46













In your case this would let you do: MainTable.create(title: 'bla', child_table_attributes: { title: 'bla2' } ) Which will create a Main Table, with a child table related to it

– Mark
Nov 20 '18 at 16:47





In your case this would let you do: MainTable.create(title: 'bla', child_table_attributes: { title: 'bla2' } ) Which will create a Main Table, with a child table related to it

– Mark
Nov 20 '18 at 16:47


















draft saved

draft discarded




















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53397174%2frails-correct-associations-for-pre-existing-models%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

Guess what letter conforming each word

Port of Spain

Run scheduled task as local user group (not BUILTIN)