Ассоциация «один ко многим» использует только один столбец внешнего ключа, а не два.
Обычно в рельсах его настройки таковы:
class Parent
has_many :children
end
class Child
belongs_to :parent
end
Фактическая ассоциация сохраняется в столбце children.parent_id
.
Допустим, у нас есть нестандартный внешний ключ:
class Parent
has_many :children, foreign_key: 'padre_id'
end
class Child
belongs_to :parent, foreign_key: 'padre_id'
end
Достаточно просто.Мы просто должны сообщить ассоциациям обеих сторон, что такое внешний ключ.Обратите внимание, что опция class_name
не требуется, если имя класса может быть выведено из имени ассоциации.
Пользовательские первичные ключи или имена таблиц также не являются проблемой, так как ActiveRecord просматривает определения классов моделей при разрешении ассоциаций.
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
Существует также универсальная проблема, связанная с сохранением записи.прежде чем вы сможете добавить в него потомков:
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
Начиная с Rails 5 belongs_to
ассоциации не являются необязательными по умолчанию.Таким образом, экземпляр child_tables
недопустим, поскольку padre_id
равен nil.
Сначала необходимо сохранить родительскую запись:
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>
Или выполнить сборку из ассоциации own_to надругая сторона:
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