Ruby on Rails字符串主键关联外键不匹配问题及解决
Rails字符串主键作为外键的外键不匹配问题及解决
问题场景
我需要让Board模型使用字符串类型的name字段作为主键,并将其作为Topic模型的外键关联。相关代码如下:
模型定义
class Board < ApplicationRecord self.primary_key = :name has_many :topics, primary_key: :name end class Topic < ApplicationRecord belongs_to :board, primary_key: :name end
数据库迁移代码
class CreateBoards < ActiveRecord::Migration[7.0] def change create_table :boards, id: :string, primary_key: :name do |t| t.timestamps end create_table :topics do |t| t.belongs_to :board, foreign_key: true, type: :string t.timestamps end end end
遇到的错误
- 可正常创建
Board对象:b = Board.create(name: "test") - 可通过关联初始化
Topic对象:t = b.topics.new,初始化后的对象显示board_id为"test" - 但调用
t.save时触发SQLite外键不匹配错误:
irb(main):002:0> t = b.topics.new => #<Topic:0x0000558d9e1ddea0 id: nil, board_id: "test", created_at: nil, updated_at: nil> irb(main):003:0> t.save TRANSACTION (0.2ms) begin transaction Topic Create (1.0ms) INSERT INTO "topics" ("board_id", "created_at", "updated_at") VALUES (?, ?, ?) [["board_id", "test"], ["created_at", "2023-08-01 09:57:21.876835"], ["updated_at", "2023-08-01 09:57:21.876835"]] TRANSACTION (0.1ms) rollback transaction /var/lib/gems/3.0.0/gems/sqlite3-1.6.3-x86_64-linux/lib/sqlite3/database.rb:152:in `initialize': SQLite3::SQLException: foreign key mismatch - "topics" referencing "boards" (ActiveRecord::StatementInvalid) /var/lib/gems/3.0.0/gems/sqlite3-1.6.3-x86_64-linux/lib/sqlite3/database.rb:152:in `initialize': foreign key mismatch - "topics" referencing "boards" (SQLite3::SQLException)
奇怪的是,直接在数据库控制台执行相同的INSERT语句却能成功插入,后续也能在Rails控制台检索并保存该Topic。
已找到的解决方案
切换到Postgres数据库后,错误的列引用问题更清晰,最终通过修改迁移代码中foreign_key选项的传参方式解决:
t.belongs_to :board, foreign_key: {primary_key: :name}, type: :string
修改后所有操作恢复正常。
疑问解答
关于你提到的Rails文档未提及:foreign_key选项可传入哈希的问题:
这属于文档的细节遗漏情况,并非罕见。实际上t.belongs_to的:foreign_key选项会将参数透传给底层的add_foreign_key方法,而add_foreign_key本身支持传入哈希来配置外键的细节(比如指定关联的主键列、外键名称等)。官方文档通常会在add_foreign_key的说明里明确这些参数,但在t.belongs_to的文档中可能不会重复列举所有可传递的嵌套参数。
内容的提问来源于stack exchange,提问作者MP5 Lover
相关产品推荐
相关产品推荐

