This is my code
class CreatePosts < ActiveRecord::Migration[5.1]
def change
create_table :posts, id: :uuid do |t|
t.string :name
t.references :user, type: :uuid
t.references :user, type: :uuid, foreign_key: true
t.timestamps
end
end
end
I am confused what is the difference b/w these line.Both lines of code are working fine to accomplish reference between table .
t.references :user, type: :uuid
t.references :user, type: :uuid, foreign_key: true #what this line is doing
Can anybody explain me when to use foreign_key or not .
I find the similar things while searching these
t.references :makers, foreign_key: { to_table: :office }
In above code foreign_key is not true. It referencing to some table. why is it so.
You can check the document of references here, it uses same options with add_reference.
So, the different is:
t.references :user, type: :uuid - Add a column without adding constraint.
t.references :user, type: :uuid, foreign_key: true - Add a column and foreign key constraint. If you don't specify foreign_key, so it will be false.
foreign_key: { to_table: :table_name } - It's option to add a column with a custom name instead of convention name.
For example, in document:
add_reference(:products, :supplier, foreign_key: {to_table: :firms})
so, it will add a column name supplier_id to table products and add a foreign key to reference to firms table.
If you follow convention name, then you will want to add a column named firm_id instead of supplier_id.
foreign_key: true will create a foreign key constraint, and without it ONLY the foreign key will be created.
to understand the difference between foreign key and foreign key constraint please visit this link.
specifying foreign_key: { to_table: :office } will make the foreign key reference the office table.
Related
recently I have a migration that adds a user_id column to the watch_events tables. and thus I want to change the watch_event models to handle belongs_to but with multiple approach
create_table 'users', force: :cascade do |t|
t.integer 'id'
t.integer 'customer_id'
end
create_table 'watch_events', force: :cascade do |t|
t.integer 'customer_id'
t.integer 'user_id'
end
previously
class WatchEvent < ApplicationRecord
belongs_to :user, foreign_key: :customer_id, primary_key: :customer_id
end
what I want:
if watch_event.customer_id is present, i want to use belongs_to :user, foreign_key: :customer_id, primary_key: :customer_id
if watch_event.customer_id is not present, i want to use normal belongs_to :user
how can I achieve this on the watch_event model?
I do not think that Rails supports 'fallback foreign keys' on associations. However, you can write a simple wrapper for your problem. First, relate your WatchEvent class twice to the user model, using your two keys and two 'internal' association names (:user_1 and :user_2). Then, add a 'virtual association reader' (user) and a 'virtual association setter' (user=(new_user)):
class WatchEvent < ApplicationRecord
belongs_to :user_1,
class_name: 'User',
foreign_key: :customer_id
belongs_to :user_2,
class_name: 'User',
foreign_key: :user_id
def user
user_1 || user_2
end
def user=(new_user)
self.user_1 = new_user
end
end
With this solution, the requirements "use customer_id to find user" and "use user_id as fallback foreign key if customer_id is nil or doesn't yield a result" is satisfied. It happens in the association reader method user. When there is a reader method, you'll need a setter method, which is user=(). Feel free to design the setter's internals as required, mine is just a suggestion.
BTW: You may need to re-add the declaration of the foreign primary_key. I omitted that for clarity.
If I understand your question correctly, then what you are looking for is a Polymorphic association.
If you see the code below, what it basically does is create two columns in the watch_events table, watcher_type and watcher_id. And the belongs_to :watcher then uses the watcher_type column to identify which model it should associate to,
create_table 'watch_events', force: :cascade do |t|
t.references 'watcher', polymorphic: true, null: false
end
class WatchEvent < ApplicationRecord
belongs_to :watcher, polymorphic: true
end
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:
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.
How can I generate a foreign key to reference to the string primary key "isbn" in "book" table?
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)
What is the difference between "add_foreign_key" and "add_reference" methods in rails?
According to rails official guide all I understand is that they both are used to create foreign key constraint between two tables.
add_foreign_key - adds a new foreign key. from_table is the table with the key column, to_table contains the referenced primary key.
add_reference - is meant as a shortcut for creating a column, index and foreign key at the same time.
What is foreign key - a foreign key is a field or group of fields in a table that uniquely identifies a row in another table.
(Note: This answer is based on Rails 6.0.)
In a word, add_reference (Ref) is kind of a short-form of a combined set of add_column, add_index, and add_foreign_key (Ref) without adding a DB-level foreign key in default. So, when you want to achieve something simple enough or (conversely?) a polymorphic reference, add_reference is handy. If not, use add_foreign_key, maybe combined with explicit add_index.
As a simple example, these two are (I think) equivalent to each other:
add_reference :articles, :author, foreign_key: true
add_column :articles, :author_id, :bigint, null: true
add_foreign_key :articles, :authors
add_index :articles, :author_id
Here are more detailed differences:
The second argument of add_reference is a reference (column name without _id, hence usually singular), whereas that of add_foreign_key is a table name (hence usually plural).
In add_reference,
DB-level foreign key is not created in default, unless foreign_key option is specified non-nil.
index: true is default, whereas the index is irrelevant in add_foreign_key
null: true is default (allowing nulls for the column), which is irrelevant in add_foreign_key
polymorphic: true is available only with add_reference in Rails (which will create 2 columns in one action; see Ref).
The formats of the accepted options between the two are totally different, though add_reference is largely more inclusive, accepting a wider range of options.
Two example realistic use-cases
For the has_one association, where null is forbidden:
add_reference :products, :merchant, null: false, index: {unique: true}, foreign_key: {on_delete: :cascade}
When a table has 2 foreign-key columns to an identical table:
add_foreign_key :products, :merchants, column: :seller_id
add_foreign_key :products, :merchants, column: :buyer_id
add_index :products, [:seller_id, :buyer_id], unique: true, name: 'index_my_name_shorter_than_64chars'
There is a limitation to add_reference compared to add_foreign_key.
As I am curious if there is a way to do the exact following thing with add_reference. Afaik the standard foreign_key to primary_key/reference_key mapping can not be diverged with add_reference.
Migration snippet
add_foreign_key :foos, :bars, column: :foo_key, primary_key: :foo_key, type: :string
add_index :foos
Usecase is when trying to map the foreign_key to the primary_key in a non Standard way. Let's say using a table with STI to hold multiple references
So I read this question, answer and the comments, but it doesn't answer my case, which is what to do when of the columns is a foreign key?
Here is my original migration to create the table in question:
class CreateTemplates < ActiveRecord::Migration[5.1]
def change
create_table :templates, id: :uuid do |t|
t.references :account, type: :uuid, foreign_key: true
t.string :name
t.text :info
t.string :title
t.timestamps
end
end
end
Since account_id is a foreign_key (and identifies the customer) it will appear in almost all (99%) of queries on this table.
Now it has been decided that name should be unique to account, so the model has been updated:
validates_uniqueness_of :name, scope: [:account]
So once I add the joint index:
add_index :templates, [:name, :account_id], unique: true
should I delete the index on account_id?
I ask because in SQLLite (see this), it seems the answer would be that I don't need the single index on account_id and to create my new index with account_id in the first position:
add_index :templates, [:account_id, :name], unique: true
I'm using postgres, so does the same idea apply?
You have to add extra index if it's not the first index.
So if you have this:
add_index :templates, [:name, :account_id], unique: true
then you should not delete the original :account_id foreign key index, since it is second index.
I recommend you to read about index implementations. It's pretty interesting and you can learn a lot from it.
add_reference :books, :author
add_column :books, :author_id, :integer
Here add references will create user_id column and add column is also creating user_id column in books table. What is the difference between them. What is the advantage of using references instead of column?
TLDR
#add_column is meant for adding a column like the name suggests.
#add_reference is meant as a shortcut for creating a column, index and foreign key at the same time.
Explanation
In your example the only difference is the index on the column that will be created by #add_reference that defaults to true.
add_reference :books, :author
# equals
add_column :books, :author_id, :integer
add_index :books, :author_id
But if you would take the following line:
add_reference :books, :author, foreign_key: true
It would also create a foreign key constraint.
Furthermore if you would like to have every author be able to publish only one book you can set the unique constraint through #add_reference by doing the following:
add_reference :books, :author, null: false, index: {unique: true}, foreign_key: true
This requires every book to have an author and restraints the authors to have a maximum of one book.
The same can be done using #add_column by doing the following:
add_column :books, :author_id, :integer, null: false
add_index :books, :author_id, unique: true
add_foreign_key :books, :authors
Both will generate the same columns when you run the migration.
The first command adds a belongs_to :author relationship in your Book
model whereas the second does not. When this relationship is
specified, ActiveRecord will assume that the foreign key is kept in
the author_id column and it will use a model named Author to
instantiate the specific author.
The first command also adds an index on the new author_id column.