Ken Tsoi
Ken Tsoi

Reputation: 1303

Ruby on Rails: how to create a reference to a db table with string as primary key

I am new to ROR, and now doing a course project related to books. I have a "book" table with string 'isbn' as its primary key, and now trying to add another "comment" with reference to the book table with a foreign key 'isbn' to refer to the "book" table . So my "models/comment.rb" looks like this:

class Comment < ApplicationRecord
  belongs_to :book, references: :isbn, type: :string
end 

And "models/book.rb" is:

class Book < ApplicationRecord
  has_many :comments, dependent: :destroy
end

My "books" table looks like: SQLite view for books table I would expect that the "comments" table generated will contain a column "isbn" (string) after db migration, but in fact the "comments" table generated contained a "book_id" (integer) instead. enter image description here How can I generate a foreign key to reference to the string primary key "isbn" in "book" table?

Upvotes: 0

Views: 1293

Answers (1)

max
max

Reputation: 101851

Start by setting up the foreign key column to be the right type and set the foreign key constraint to point to the right column:

class CreateComments < ActiveRecord::Migration[6.0]
  def change
    create_table :comments do |t|
      t.string :title
      t.string :note
      t.references :book, type: :string, column_name: :isbn
      t.timestamps
    end
  end
end

I would expect that the "comments" table generated will contain a column "isbn" (string) after db migration, but in fact the "comments" table generated contained a "book_id" (integer) instead.

Migrations are actually a lot simpler and dumber than you would think. Its just a DSL that creates SQL statements. Its not actually in any way aware of the other table so it has no way of knowing that books.isbn is a string column. It just uses the default type for add_reference which is :integer. Rails is driven by convention - not artificial intelligence.

If you want to call the column something else like book_isbn you have to do it in two steps instead:

class CreateComments < ActiveRecord::Migration[6.0]
  def change
    create_table :comments do |t|
      t.string :title
      t.string :note
      t.string :book_isbn
      t.timestamps
    end
    add_foreign_key :comments, :books, 
       column: "book_isbn", primary_key: "isbn"
  end
end

Then setup the books model to use your non-conventional primary key:

class Book < ApplicationRecord
  self.primary_key = :isbn
  has_many :comments, 
    foreign_key: :book_isbn # default is book_id
end

On the comment side you need to configure the belongs_to association so that it points to books.isbn instead of books.id.

class Comment < ApplicationRecord
  belongs_to :book, primary_key: :isbn
end

references is not a valid option for belongs_to.

ArgumentError (Unknown key: :references. Valid keys are: :class_name, :anonymous_class, :foreign_key, :validate, :autosave, :foreign_type, :dependent, :primary_key, :inverse_of, :required, :polymorphic, :touch, :counter_cache, :optional, :default)

Upvotes: 3

Related Questions