微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Rails 5 with PostgreSQL 将表关系从多态更改为标准 has_one/belongs_to 关联

如何解决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_idkey_type)选择的名称,它仍然可以指代 Key 模型并被称为其他名称例如 source_idsource_type 这就是 Rails 要求您指定它的原因。

运行上述步骤的结果: Results from code above

关于去除多态关系

数据库本身不知道您的关系是否是多态的。在没有用于确保数据完整性的外键的情况下,常规关系可以正常工作。

您可能收到错误 PG::UndefinedColumn: ERROR: column cars.key_type does not exist 的原因是 Rails 试图使用迁移后不再存在的 key_type 列进行查询以将其删除。

我怀疑您可能没有在运行迁移后立即从 polymorphic: true 模型中删除 Car,或者 Rails 已经加载了模型,认为它是多态的并且没有正确重新加载.以下步骤应该可以将迁移从多态切换到非多态:

  1. 从具有多态关系的原始状态的服务器开始,运行以下迁移。请注意,此步骤是可选的,但建议执行:
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
  1. 现在将您的模型关系更改为以下内容:
class Key < ApplicationRecord
    has_one :car,dependent: :destroy
end

class Car < ApplicationRecord
    belongs_to :key
end
  1. 重新启动服务器或控制台会话。

让我知道这是否有效!我没有机会运行代码,所以可能存在一些语法错误。

运行上述步骤的结果: Results from running steps above

,

奇怪,你能不能尝试将新的 key_id 列修改为引用 Key 表的外键?

尽管在我看来问题是 ActiveRecord 而不是 PostgreSQL,因为 ActiveRecord 正在生成一个假设多态关系的查询。话虽如此,如果您使用 spring 从模型中删除多态关系后,我建议在打开新控制台之前用 spring stop 停止它。我发现了一些奇怪的问题,例如停止 spring 后修复的问题。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。