【问题标题】:Custom name foreign key in Rails causing 'ActiveRecord::InvalidForeignKey: PG::ForeignKeyViolation' errorRails 中的自定义名称外键导致“ActiveRecord::InvalidForeignKey:PG::ForeignKeyViolation”错误
【发布时间】:2019-11-13 04:11:44
【问题描述】:

我有一个Book 模型和一个User 模型。我正在尝试在books 表中创建一个名为author_id 的新列,它实际上是users 表的外键。

我关注了this article by Joshua Frankel。但我收到以下错误

ActiveRecord::InvalidForeignKey: PG::ForeignKeyViolation: ERROR`

我正在使用 Rails4.2


db/migrate/20191112111409_add_author_to_books.rb:

class AddAuthorToBooks < ActiveRecord::Migration
  def up
    add_reference :books, :author, references: :users, index: false
    commit_db_transaction
    add_index :books, :author_id, algorithm: :concurrently
    add_foreign_key :books, :users, column: :author_id
  end

  def down
    remove_column :books, :author_id
  end
end

app/models/book.rb:

class Book < ActiveRecord::Base
  belongs_to :author, class_name: :User, foreign_key: :author_id
end

app/models/user.rb:

class User < ActiveRecord::Base
  has_many :books, foreign_key: :author_id
end

Rails 控制台中的错误:

> user = FactoryBot.create(:user)
> book = FactoryBot.create(:book)
> book.author = user
> book.save
ActiveRecord::InvalidForeignKey: PG::ForeignKeyViolation: ERROR:  insert or update on table "books" violates foreign key constraint "fk_rails_13be98de92"
DETAIL:  Key (author_id)=(1) is not present in table "users".
: UPDATE "books" SET "author_id" = $1, "updated_at" = $2 WHERE "books"."id" = $3
from ~/.ruby-gemset/ruby/2.3.0/gems/activerecord-4.2.11.1/lib/active_record/connection_adapters/postgresql_adapter.rb:602:in `exec_prepared'
Caused by PG::ForeignKeyViolation: ERROR:  insert or update on table "books" violates foreign key constraint "fk_rails_13be98de92"
DETAIL:  Key (author_id)=(1) is not present in table "users".

from ~/.ruby-gemset/ruby/2.3.0/gems/activerecord-4.2.11.1/lib/active_record/connection_adapters/postgresql_adapter.rb:602:in `exec_prepared'
>

【问题讨论】:

    标签: ruby-on-rails ruby postgresql activerecord foreign-keys


    【解决方案1】:

    我发现了问题。它与 Postgres 中的模式有关。

    我在 Postgres 数据库中使用 Apartment gem 进行租赁。在我的例子中,books 表是租用的。但是,users 表没有被租用——它们是通用的。意思是,user 存在于public 模式中,但book 存在于(假设)Malaysia 模式中。

    因此,当我们尝试保存 book.save! 时,books 表(位于 Malaysia 架构中)会在同一架构的 users 表(即 Malaysia架构)。但是,由于我的用户实际上存在于 public 架构中,它认为没有 id 为 1 的用户(因为它只在 Malaysia 架构中寻找它)。

    然后我遇到了这个known issue in Apartment,关于跨模式的外键。


    我是怎么解决的?

    • 我通过删除新的author_id 列将我的数据库迁移一步回滚到基本上恢复我的更改。
    • 然后从迁移文件中删除 add_foreign_key 语句,这样 Postgres 表就不会创建任何 db 级外键关联。
    • 然后再次运行迁移。

    所以现在,我确实有一个名为 author_id 的新列(也已编入索引),但它没有 users 表的 DB 级外键。然后规范就顺利通过了。

    因此,它不再是真正的一流 db 级外键。它只是依赖于模型中定义的 Rails 关联来实现我们想要的 "foreign-key-sque" 行为 - 效果很好。

    【讨论】:

    • 很高兴听到您在这里解决了这个问题。我留下了一个简化的迁移,下次您需要将并发索引、引用和 foreign_key 添加到自定义命名列时可能会有所帮助:joshfrankel.me/blog/…
    • 谢谢@JoshFrankel。感谢帮助:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-23
    • 1970-01-01
    • 2011-02-01
    • 2016-03-09
    • 1970-01-01
    相关资源
    最近更新 更多