AssociationTypeMismatch的解决方法?

时间:2019-05-07 19:08:14

标签: ruby-on-rails activerecord refactoring associations

我正处于重构过程中,在那里我为将要出现在功能标记后面的新传入数据编写了新模型,同时为那些尚不具备该功能的人同时为输入数据保持旧模型处于活动状态旗。旧模型和新模型都与数据库中的同一表进行交互。


class Poem < ApplicationRecord
   belongs_to: :author
   belongs_to: :anthology, optional: true
end

class Anthology < ApplicationRecord
   belongs_to: :author
   has_many: :poems
end

class Author < ApplicationRecord
   has_many: :anthologies
   has_many: :poems
end


class NewPoem < ApplicationRecord
   self.table_name = 'poems'

   belongs_to: :author, class_name: 'NewAuthor'
   belongs_to: :anthology, class_name: 'NewAnthology', optional: true
end

class NewAnthology < ApplicationRecord
   self.table_name = 'anthologies'

   belongs_to: :author, class_name: 'NewAuthor'
   has_many: :poems, class_name: 'NewPoem'
end

class NewAuthor < ApplicationRecord
   self.table_name = 'authors'
   has_many: :anthologies, class_name: 'NewAnthology'
   has_many: :poems, class_name: 'NewPoem'
end

正在创建新书时,我想将新书分配给作者

anthology = Anthology.find(1)
@poem = NewPoem.new
@poem.author = anthology.author

哪个给我一个错误

ActiveRecord::AssociationTypeMismatch (NewAuthor(#70008442274840) expected, got Author(#47031421032620)):

有什么办法可以解决这个问题?还是在没有数据迁移的情况下无法将模型与旧模型关联?

3 个答案:

答案 0 :(得分:0)

您需要更改获取Anthology记录的第一行。当前,它以Anthology的形式获取anthology = Anthology.find(1)模型的记录。但是,此模型引用旧的Author模型。
在下一行中,@poem = NewPoem.new实例化了NewPoem模型的对象,下一行尝试为其分配作者。但是,@poem.author将是类NewAuthor的对象,但是它被赋予值为@poem.author = anthology.author,其中anthology.author仍指旧的Author表。

将第一行更改为anthology = NewAnthology.find(1)将解决此问题。由于它们在同一张表上进行查询,因此它将获取与Anthology.find(1)相同的记录。
整个代码将变为:

anthology = NewAnthology.find(1)
@poem = NewPoem.new
@poem.author = anthology.author

答案 1 :(得分:0)

看起来新模型只是旧模型的超集。您可以将NewPoem(以及其他新模型)定义为:

class NewPoem < Poem
  validates :new_colums

  def new_cool_stuff
  end
end

这样,您可以避免重新定义关联。

答案 2 :(得分:0)

ActiveRecord实际上具有内置的“单表继承”机制,您可以在此处使用。

首先在表中添加名为type的列:

class AddTypeToPoems < ActiveRecord::Migration[5.2]
  def change
    add_column :poems, :type, :string
    add_index :poems, :type
  end
end

然后将类设置为继承:

class Poem < ApplicationRecord
end

class NewPoem < Poem
  # gets its table name from the parent class
end

ActiveRecord自动设置type列,甚至在检索记录时也使用它:

irb(main):001:0> NewPoem.create
   (0.3ms)  BEGIN
  NewPoem Create (1.3ms)  INSERT INTO "poems" ("created_at", "updated_at", "type") VALUES ($1, $2, $3) RETURNING "id"  [["created_at", "2019-05-08 19:17:45.086947"], ["updated_at", "2019-05-08 19:17:45.086947"], ["type", "NewPoem"]]
   (0.7ms)  COMMIT
=> #<NewPoem id: 1, title: nil, created_at: "2019-05-08 19:17:45", updated_at: "2019-05-08 19:17:45", type: "NewPoem">
irb(main):002:0> Poem.first
  Poem Load (0.7ms)  SELECT  "poems".* FROM "poems" ORDER BY "poems"."id" ASC LIMIT $1  [["LIMIT", 1]]
=> #<NewPoem id: 1, title: nil, created_at: "2019-05-08 19:17:45", updated_at: "2019-05-08 19:17:45", type: "NewPoem">
irb(main):003:0> 

因此,即使我们使用Poem.first,它也会从结果中实例化一个NewPoem实例。如果您在子类上使用查找器,则rails会自动对其进行范围调整:

irb(main):003:0> NewPoem.all
  NewPoem Load (1.8ms)  SELECT  "poems".* FROM "poems" WHERE "poems"."type" IN ('NewPoem') LIMIT $1  [["LIMIT", 11]]

如果只希望使用“旧”类型,则需要使用:

Poem.where(type: [nil, 'Poem'])