All communication methods are built around caching, which keeps the results of recent requests available for future operations. The cache is shared between different methods. For example:
author.books # get books from database
author.books.size # use the cached copy of the books
author.books.empty? # use the cached copy of the books
But what if you want to reload the cache because the data may have been modified by another part of the application? Just call reload on the connection:
author.books # get books from database
author.books.size # use the cached copy of the books
author.books.reload.empty? # discard the cached copy of the books
# and access the database again
Name Collision Prevention
You are not free to choose any name for your connections. Since creating a relationship adds a method with that name to the model, it would be a bad idea to give the relationship a name already used as an instance method of ActiveRecord::Base. The link method will then override the base method and anything will stop working. For example, attributes or connection are bad names for relationships.
schema update
Links are very useful, but not magical. You are responsible for maintaining your database schema in accordance with the links. In practice, this means two things, depending on what type of links you are creating. For belongs_to relationships, you need to create foreign keys, and for has_and_belongs_to_many relationships, you need to create a suitable join table.
Creating Foreign Keys for belongs_to Relationships
When you declare a belongs_to relationship, you need to create foreign keys, if necessary. For example, consider this model:
classBook < ApplicationRecord
belongs_to :author
end
This declaration needs to create a suitable foreign key on the books table:
class CreateBooks < ActiveRecord::Migration[5.0]
def change
create_table :books do |t|
t.datetime :published_at
t.string :book_number
t.integer :author_id
end
end
end
If you are creating a relationship after you have already created the underlying model, you must remember to create an add_column migration to provide the required foreign key.
It is good practice to add an index on the foreign key to improve query performance, and a constraint on the foreign key to ensure referential integrity of the data:
class CreateBooks < ActiveRecord::Migration[5.0]
def change
create_table :books do |t|
t.datetime :published_at
t.string :book_number
t.integer :author_id
end
add_index :books, :author_id
add_foreign_key :books, :authors
end
end
Create join tables for has_and_belongs_to_many relationships
If you have created a has_and_belongs_to_many relationship, you must create a join table. If a join table name is not explicitly specified using the :join_table option, Active Record creates a name using the alphabetical order of the class names. Therefore, the join between the author and book models will default to the value of the table name “authors_books” since “a” comes before “b” in alphabetical order.
WARNING: Precedence between model names is calculated using the <=> operator for String. This means that if the strings are of different lengths and they are equal in their short part, then the longer string is treated as having a higher lexical precedence than the shorter one. For example, one would expect the tables “paperboxes” and “papers” to create a join table “papers_paper_boxes” because the name “paper_boxes” is longer, but would actually generate a table named “paper_boxes_papers” (because the underscore character “\” is lexicographically smaller than “s” in normal encoding).
Whatever the name, you must manually generate the join table in the appropriate migration. For example, consider these links:
class Assembly < ApplicationRecord
has_and_belongs_to_many :parts
end
class Part<ApplicationRecord
has_and_belongs_to_many :assemblies
end
Now we need to write a migration to create the assemblies_parts table. This table must be created without a primary key:
class CreateAssembliesPartsJoinTable < ActiveRecord::Migration[5.0]
def change
create_table :assemblies_parts, id: false do |t|
t.integer :assembly_id
t.integer :part_id
end
add_index :assemblies_parts, :assembly_id
add_index :assemblies_parts, :part_id
end
end
We are passing id: false to create_table since this table does not represent a model. This is necessary for the connection to work correctly. If you see strange behavior in the has_and_belongs_to_many relationship, such as malformed model IDs, or ID conflict exceptions, you most likely forgot to remove the primary key.
You can also use the create_join_table method
class CreateAssembliesPartsJoinTable < ActiveRecord::Migration[5.0]
def change
create_join_table :assemblies, :parts do |t|
t.index :assembly_id
t.index :part_id
end
end
end
Link Scope Control
By default, links only look for objects within the scope of the current module. This is important when you declare Active Record models inside a module. For example:
module MyApplication
module business
class Supplier < ApplicationRecord
has_one :account
end
class Account < ApplicationRecord
belongs_to :supplier
end
end
end
This will work since both the Supplier and Account classes are defined within the same scope. But the following won’t work because Supplier and Account are defined in different scopes:
module MyApplication
module business
class Supplier < ApplicationRecord
has_one :account
end
end
module Billing
class Account < ApplicationRecord
belongs_to :supplier
end
end
end
To link a model to a model in a different namespace, you must specify the fully qualified class name in the link declaration:
Bilateral Relations
It is normal for relationships to work in two directions, requiring a declaration in two different models:
classAuthor<ApplicationRecord
has_many:books
end
classBook < ApplicationRecord
belongs_to :author
end
Active Record will attempt to automatically determine that the two models form a bidirectional relationship based on the name of the relationship. This way, Active Record will only load one copy of the Author object, making your application more efficient and preventing inconsistent data:
a = Author.first
b = a.books.first
a.first_name == b.author.first_name # => true
a.first_name = ‘David’
a.first_name == b.author.first_name # => true
Active Record supports autodetection for most associations with standard names. However, Active Record will not automatically detect bidirectional links containing any of the following options:
:conditions
:through
:polymorphic
:class_name
:foreign_key
For example, consider the following model declarations: