Rails - 自连接与 postgresql

Anc*_*nek 2 postgresql ruby-on-rails heroku rails-activerecord

我有称为部分的自连接模型:

class Section < ApplicationRecord
    belongs_to :offer
    # Self joins:
    has_many :child_sections, class_name: "Section", foreign_key: "parent_section_id"
    belongs_to :parent_section, class_name: "Section", optional: true
end
Run Code Online (Sandbox Code Playgroud)

带有迁移文件:

class CreateSections < ActiveRecord::Migration[5.0]
  def change
    create_table :sections do |t|
      t.string :name
      t.references :offer, foreign_key: true

      t.references :parent_section, foreign_key: true
      t.timestamps
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

使用 mySql 很好,但后来我删除了数据库,将它们更改为 postresql(因此它们对 heroku 友好),并创建了新的。尝试后rails db:migrate出现错误提示:

StandardError: An error has occurred, this and all later migrations canceled:

PG::UndefinedTable: ERROR:  relation "parent_sections" does not exist
Run Code Online (Sandbox Code Playgroud)

可能发生了什么?mysql 和 postgresql 中的自联接之间有什么区别吗?

mu *_*ort 5

您的t.references电话:

t.references :parent_section, foreign_key: true
Run Code Online (Sandbox Code Playgroud)

将尝试用 PostgreSQL 做两件事:

  1. 添加一个名为 的整数列parent_section_id
  2. 在数据库内部添加外键约束以确保引用完整性(即确保parent_section_id引用部分中的值存在)。

你的问题是2。对于t.references :parent_section,FK 看起来像:

parent_section_id integer references parent_sections(id)
Run Code Online (Sandbox Code Playgroud)

因为它使用标准的 Rails 命名约定,这就是您的parent_sections错误的来源。您可以为 FK 约束指定目标表,就像您可以提供:class_nameto 一样belongs_to

t.references :parent_section, :foreign_key => { :to_table => :sections }
Run Code Online (Sandbox Code Playgroud)

此修复会触发您的下一个问题:您无法为不存在且sectionscreate_table :sections块执行完毕之前不存在的表创建 FK 。

这个问题有两种常见的解决方案:

  1. 创建包含所有列的表,然后添加 FK 约束。在你的迁移中是这样的:

    create_table :sections do |t|
      t.string :name
      t.references :offer, foreign_key: true
      t.references :parent_section
      t.timestamps
    end
    add_foreign_key :sections, :sections, :column => :parent_section_id
    
    Run Code Online (Sandbox Code Playgroud)
  2. 创建没有引用列 ( parent_section_id)的表,然后添加引用列和 FK。在你的迁移中是这样的:

    create_table :sections do |t|
      t.string :name
      t.references :offer, foreign_key: true
      t.timestamps
    end
    change_table :sections do |t|
      t.references :parent_section, :foreign_key => { :to_table => :sections }
    end
    
    Run Code Online (Sandbox Code Playgroud)