如何解决Rails 5 with PostgreSQL 将表关系从多态更改为标准 has_one/belongs_to 关联
带有 Postgresql 的 Rails 5 将表关系从多态更改为标准 has_one/belongs_to 关联。
我有一个表 Car,它与表 Key 有一个不必要的多态关联。由于多态为真, car.key 有效而 key.car 失败。我试图将这种关系改回 has_one/belongs_to,因为这种关系是不可变的,不需要多态性。
我想我可以删除表格并重新定义它,但接下来是恢复数据和所有关系的问题。
我已经构建了一个运行良好的迁移,我认为应该可以解决问题。运行迁移,然后从 Car 模型中删除多态性。 Rails 似乎对此很满意。 Postgresql 没有那么多。它仍然认为关系是多态的,并抱怨缺少 key_type 字段。请参阅以下代码和简化示例。
在导致此问题的架构外部必须存在某些触发器、过程或约束。但是,我还没有找到。
在这里,多态性是一种不必要的并发症。如何解决这个问题,无论是通过这项工作还是通过您可能提出的其他建议?
迁移:
class ResolveCarKeypolymorphism < ActiveRecord::Migration[5.2]
def self.up
add_column :cars,:will_be_key_id,:integer
self.copy_key_id_to_will_be_key_id
remove_reference :cars,:key,polymorphic: true
rename_column :cars,:key_id
end
def self.down
rename_column :cars,:key_id,:will_be_key_id
add_reference :cars,:keys,polymorphic: true
self.copy_will_be_key_id_to_key_id
remove_column :cars,:will_be_key_id
end
def copy_key_id_to_will_be_key_id
Car.all.each do |car|
car.will_be_key_id = car.key_id
car.save!
puts "Car:#{car.id};Key:#{car.key_id}"
end
end
def copy_will_be_key_id_to_key_id
Car.all.each do |car|
car.key_id = car.will_be_key_id
car.key_type = "Key"
car.save!
puts "Car:#{car.id};Key:#{car.key_id}"
end
end
end
示例代码:
class Key < ApplicationRecord
has_one :car,dependent: :destroy
end
class Car < ApplicationRecord
belongs_to :key,dependent: :destroy #,polymorphic: true (commented out after the migration)
end
car = Car.find_by(stock_number: "PRT38880")
=> #<Car id: 56251,stock_number: "PRT38880",key_id: 25629>
car.key
=> #<Key id: 25629>
key = Key.find(25629)
=> #<Key id: 25629>
key.car
=> PG::UndefinedColumn: ERROR: column cars.key_type does not exist
附加示例:(我很惊讶这有效,但仅在迁移和回滚之后。)
car = Car.find_by(stock_number: "PRT38880")
=> #<Car id: 56251,key_id: 25629>
key = car.key
=> #<Key id: 25629>
key.car
=> #<Car id: 56251,key_id: 25629>
替代迁移用相同的结果替换整个表(更新):
class ResolveCarKeypolymorphism < ActiveRecord::Migration[5.2]
def self.up
create_table "cars_news",id: :serial,force: :cascade do |t|
t.string "stock_number",limit: 255,default: "",null: false
... additional fields
t.integer "key_id"
end
add_foreign_key :cars,:keys
self.copy_cars_to_cars_news
drop_table :cars
rename_table :cars_news,:cars
change_table :cars do |t|
t.index ["company_id"],name: "index_cars_on_company_id"
t.index ["stock_number"],name: "index_cars_on_stock_number"
t.index ["key_id"],name: "index_cars_on_key_id"
end
end
def self.down
remove_foreign_key :cars,:keys if foreign_key_exists?(:cars,:keys)
remove_index :cars,:key_id if index_exists?(:keys,:key_id)
add_column :cars,:key_type,:string unless column_exists?(:cars,:key_type)
add_index :cars,["key_type","key_id"],name: "index_cars_on_key_type_and_key_id"
Car.update_all(key_type: "Key")
end
def copy_cars_to_cars_news
Car.all.each do |car|
cars_new = CarsNew.new
car.attributes.each do |key,value|
cars_new[key] = value unless key == "key_type"
end
cars_new.save!
puts "Car:#{cars_new[:stock_number]}:#{cars_new[:id]} created with key_id:#{cars_new[:key_id]};"
end
end
end
解决方法
关于查询key.car
应该可以使用多态关联来建立双向关系。我相信,在你的情况下,语法如下:
class Key < ApplicationRecord
has_one :car,as: :key,dependent: :destroy
end
class Car < ApplicationRecord
belongs_to :key,polymorphic: true
end
注意 as: :key
。在这种情况下,:key
指的是您为多态列(又名 key_id
和 key_type
)选择的名称,它仍然可以指代 Key
模型并被称为其他名称例如 source_id
和 source_type
这就是 Rails 要求您指定它的原因。
关于去除多态关系
数据库本身不知道您的关系是否是多态的。在没有用于确保数据完整性的外键的情况下,常规关系可以正常工作。
您可能收到错误 PG::UndefinedColumn: ERROR: column cars.key_type does not exist
的原因是 Rails 试图使用迁移后不再存在的 key_type
列进行查询以将其删除。
我怀疑您可能没有在运行迁移后立即从 polymorphic: true
模型中删除 Car
,或者 Rails 已经加载了模型,认为它是多态的并且没有正确重新加载.以下步骤应该可以将迁移从多态切换到非多态:
- 从具有多态关系的原始状态的服务器开始,运行以下迁移。请注意,此步骤是可选的,但建议执行:
class ResolveCarKeyPolymorphism < ActiveRecord::Migration[5.2]
def change
remove_index :cars,column: [:key_type,:key_id]
remove_column :cars,:key_type,:string
add_index :cars,:key_id
end
end
- 现在将您的模型关系更改为以下内容:
class Key < ApplicationRecord
has_one :car,dependent: :destroy
end
class Car < ApplicationRecord
belongs_to :key
end
- 重新启动服务器或控制台会话。
让我知道这是否有效!我没有机会运行代码,所以可能存在一些语法错误。
,奇怪,你能不能尝试将新的 key_id
列修改为引用 Key 表的外键?
尽管在我看来问题是 ActiveRecord 而不是 PostgreSQL,因为 ActiveRecord 正在生成一个假设多态关系的查询。话虽如此,如果您使用 spring 从模型中删除多态关系后,我建议在打开新控制台之前用 spring stop
停止它。我发现了一些奇怪的问题,例如停止 spring 后修复的问题。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。