Rails-先前模型的正确关联

时间:2018-11-20 16:16:18

标签: ruby-on-rails

我正在尝试为一些预先存在的数据库表创建Rails模型。

有1个主表,其中有1个子表。

问题可能是每个表上“外键”的名称不同

  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

鉴于主表每个子表仅具有1条记录,但是每个主表可能有许多子记录-这些关联看起来正确吗?

我希望能够做类似的事情:

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

m.save!

编辑:由于我更新了上面的代码,因此我不再遇到错误。现在它可以工作,但是我必须在两个表上分配ID。 如果我在主对象上分配ID,它将自动在fk列中为子对象提供该ID 即

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

但是:

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: 但是我得到了错误:

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

我想念什么吗?

2 个答案:

答案 0 :(得分:1)

一对多关联仅使用一个外键列,而不是两个。

通常在Rails中这样设置:

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为nil,因此padre_id实例无效。

您要么需要先保存父记录:

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> 

或者从另一端的belongs_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

答案 1 :(得分:0)

不确定这是否是您面临的问题,但是目前您正在初始化新的MainTables和ChildTables,但是您没有保存它们。

这意味着不会为它们分配ID,这又意味着当您尝试向主表分配子表时,它没有主表(或本身)的ID以创建联接记录。

在保存子记录之前尝试保存记录