我正处于重构过程中,在那里我为将要出现在功能标记后面的新传入数据编写了新模型,同时为那些尚不具备该功能的人同时为输入数据保持旧模型处于活动状态旗。旧模型和新模型都与数据库中的同一表进行交互。
即
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)):
有什么办法可以解决这个问题?还是在没有数据迁移的情况下无法将模型与旧模型关联?
答案 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'])