Active records association BY ANANTA RAJ LAMICHHANE
Active records association
BYANANTA RAJ LAMICHHANE
Active record?
facilitates the creation and use of business objects whose data requires persistent storage to a database.
is a description of an Object Relational Mapping system.ORM, is a technique that connects the rich objects of an application to tables
in a relational database management system.
Convention over Configuration
Naming conventionDatabase Table - Plural with underscores separating words (e.g.,
book_clubs).Model Class - Singular with the first letter of each word capitalized (e.g.,
BookClub).Schema convention Foreign keys - These fields should be named following the pattern
singularized_table_name_id.Primary keys - By default, Active Record will use an integer column named
id as the table's primary keyoptional column names like created_at, updated_at, type etcs….
AR associations. Why?
make common operations simpler and easier
Scenarioconsider a simple Rails application that
includes a model for customers and a model for orders.
Each customer can have many orders
rails g model customer name:stringrails g model order customer_id:integer
description:stringrake db:migraterails c
Without associationclass Customer < ActiveRecord::Baseendclass Order < ActiveRecord::Baseend
2.1.1 :001 > c=Customer.create(name:'cust1') => #<Customer id: 1, name: "cust1", created_at: "2015-01-03 07:58:03", updated_at: "2015-01-03 07:58:03">To Add new Order2.1.1 :002 > o=Order.new2.1.1 :003 > o.customer_id=c.id2.1.1 :003 > o.description="this is a test description"2.1.1 :006 > o.save
To deleting a customer, and ensuring that all of its orders get deleted as well2.1.1 :009 > orders= Order.where(customer_id: c.id)2.1.1 :010 > orders.each do |order|2.1.1 :011 > order.destroy2.1.1 :012?> end2.1.1 :013 > c.destroy
With Associationclass Customer < ActiveRecord::Base has_many :orders, dependent: :destroyend
class Order < ActiveRecord::Base belongs_to :customerend
2.1.1 :001 > c=Customer.create(name:'cust1') => #<Customer id: 1, name: "cust1", created_at: "2015-01-03 07:58:03", updated_at: "2015-01-03 07:58:03">To Add new Order2.1.1 :002 > o=c.orders.create(description:”this is first order”)To deleting a customer, and ensuring that all of its orders get deleted as well2.1.1 :003 > c.destroy
Type of Association
belongs_tohas_onehas_manyhas_many :throughhas_one :throughhas_and_belongs_to_many
The belongs_to Association
a one-to-one connection, such that each instance of the declaring model "belongs to" one instance of the other model.
must use the singular term
The has_one Associationhas_one association also sets up a one-to-one connection with another
model
class Customer < ActiveRecord::Base has_one :order, dependent: :destroyend
class Order < ActiveRecord::Base belongs_to :customerend
c.create_order(description:"ccc")c.order
The has_many Association
a one-to-many connection.the "other side" of a belongs_to association.The name of the other model is pluralized
when declaring a has_many association.
The has_many :through Association
a many-to-many connection
rails g model physician name:stringrails g model patient name:stringrails g model appointment physician_id:integer patient_id:integer
description:string
rake db:migraterails c
class Physician < ActiveRecord::Base has_many :appointments has_many :patients, :through => :appointmentsend
class Patient < ActiveRecord::Base has_many :appointments has_many :physicians, :through => :appointmentsend
class Appointment < ActiveRecord::Base belongs_to :physician belongs_to :patientend
2.1.1 :010 > p=Physician.find(1) => #<Physician id: 1, name: "physicain", created_at: "2015-01-04 12:19:37", updated_at: "2015-01-04 12:19:37">2.1.1 :012 > p.patients.create(name: "p2") => #<Patient id: 2, name: "p2", created_at: "2015-01-04 12:43:22", updated_at: "2015-01-04 12:43:22">2.1.1 :024 > p.appointments.where(patient_id: 2)[0].update_attributes(description:'test')
Alternatively,p.appointments.create(patient_id: 2, description: ‘this is a test description’)
2.1.1 :030 > ph=Physician.find(1) => #<Physician id: 1, name: "physicain", created_at: "2015-01-04 12:19:37", updated_at: "2015-01-04 12:19:37">2.1.1 :031 > ph.patients << Patient.all
The has_one :through Association
a one-to-one connection with another model.declaring model can be matched with one instance of
another model by proceeding through a third model. if each supplier has one account, and each account is
associated with one account history, then the supplier model could look like this:
The has_and_belongs_to_many Association
creates a direct many-to-many connection with another model, with no intervening model.
Classwork 1: Create two tables husband and wife with one on one relationship and test things.
Create a rails app which should have a database named 'blog_db' in MySQL. Then do the following1. Create models Articles (title, content), Comments (comments, article_id), Tags (name)2. Article should have many comments and each comment should be associated to one article3. Each article can have many tags and each tag may be assigned to many articles.4. Create the relation such that if we delete an article then all the comments associated with that
article is also deleted5. Create a scope to query all the articles of yesterday sorted descending according to created
date
Various options for relations
class_namedependentpolymorphicvalidatewherethrough
class_name: Controlling association scopeBy default, associations look for objects only within the current module's scope. For example:module MyApplication module Business class Supplier < ActiveRecord::Base has_one :account end class Account < ActiveRecord::Base belongs_to :supplier end endend
Supplier and Account are defined in different scopes:module MyApplication module Business class Supplier < ActiveRecord::Base has_one :account end end module Billing class Account < ActiveRecord::Base belongs_to :supplier end endend
will not work!!
module MyApplication module Business class Supplier < ActiveRecord::Base has_one :account, class_name: "MyApplication::Billing::Account" end end module Billing class Account < ActiveRecord::Base belongs_to :supplier, class_name: "MyApplication::Business::Supplier" end endend
orif an order belongs to a customer, but the
actual name of the model containing customers is Patron, you'd set things up this way:
class Order < ActiveRecord::Base belongs_to :customer, class_name:
"Patron"end
:validate
If you set the :validate option to true, then associated objects will be validated whenever you save this object. By default, this is false: associated objects will not be validated when this object is saved.
class User belongs_to :account validates :account, :presence => trueend.
class Order < ActiveRecord::Base belongs_to :customer, -> { where active: true }, dependent: :destroyend
Single table inheritance
allows inheritance by storing the name of the class in a column that is named “type” by default.
a way to add inheritance to your models. STI lets you save different models inheriting from the
same model inside a single tableRef: http://samurails.com/tutorial/single-table-inheritance-
with-rails-4-part-1/
rails new sti --no-test-frameworkrails g model animal name:string age:integer
race:stringrake db:migrate
# app/models/animal.rbclass Animal < ActiveRecord::Base belongs_to :tribe self.inheritance_column = :race
# We will need a way to know which animals # will subclass the Animal model def self.races %w(Lion WildBoar Meerkat) end
endNote that self.inheritance_column = :race is used to
specify the column for STI and is not necessary if you are using the default column type.
If you want to disable Single Table Inheritance or use the type column for something else, you can use self.inheritance_column = :fake_column.
# app/models/wild_boar.rbclass WildBoar < Animalend# app/models/meerkat.rbclass Meerkat < Animalend# app/models/lion.rbclass Lion < Animalend
Lion.create(name:"himba", age:11)Meerkat.create(name:"meerkat")WildBoar.create(name:"wildboar")2.1.1 :016 > Animal.all=> # [#<Lion id: 1, name: "himba", age: "11",race:
"Lion", created_at: "2015-01-03 05:22:49", updated_at: "2015-01-03 05:22:49">,
#<Animal id: 2, name: "ananta", age: nil, race: nil, created_at: "2015-01-03 05:24:02", updated_at: "2015-01-03 05:24:02">,
#<Meerkat id: 3, name: "wildboar", age: nil, race: "Meerkat", created_at: "2015-01-03 05:25:03", updated_at: "2015-01-03 05:25:03">,
#<WildBoar id: 4, name: "wildboar", age: nil, race: "WildBoar", created_at: "2015-01-03 05:25:50", updated_at: "2015-01-03 05:25:50">]>
Add scopes to the parent models for each child model
class Animal < ActiveRecord::Base scope :lions, -> { where(race: 'Lion') } scope :meerkats, -> { where(race: 'Meerkat') } scope :wild_boars, -> { where(race: 'WildBoar') }
---end
2.1.1 :001 > Animal.lions Animal Load (0.2ms) SELECT "animals".* FROM "animals" WHERE "animals"."race" = 'Lion' => #<ActiveRecord::Relation [#<Lion id: 1, name: "himba", age: "11", race: "Lion", created_at: "2015-01-03 05:22:49", updated_at: "2015-01-03 05:22:49">]>2.1.1 :002 > Animal.meerkats
Polymorphic associationref: http://www.millwoodonline.co.uk/blog/polymorphic-associations-ruby-on-rails
Scenario:Imagine a school or college system where both students
and teachers can add posts. The post has an author->author could be a student or
a teacher. The students and teachers can ->add many posts.
Therefore we need an association between the Student, Teacher and Post models.
$ rails generate model student name:string email:string$ rails generate model teacher name:string email:string office:integer$ rails generate model post author:references{polymorphic}:index title:string
body:textNote: created the post model with the author reference set as polymorphic
and an index
simplified by using the t.references formauthor:references{polymorphic}:index class CreatePosts < ActiveRecord::Migration def change
create_table :posts do |t| t.references :author, polymorphic: true,
index: true t.string :title t.text :body t.timestamps
end endend
class CreatePosts < ActiveRecord::Migration def change create_table :posts do |t| t.string :title t.text :body t.integer :author_id t.string :author_type t.timestamps end add_index :posts, :author_id endend
The Post model will already be setup correctly# app/models/post.rbclass Post < ActiveRecord::Base belongs_to :author, polymorphic: trueend # app/models/student.rbclass Student < ActiveRecord::Base has_many :posts, as: :authorend
# app/models/teacher.rbclass Teacher < ActiveRecord::Base has_many :posts, as: :authorend
2.1.1 :019 > s=Student.create(name: ‘Ananta’)2.1.1 :019 > s=Student.find(1)2.1.1 :011 > p=Post.new => #<Post id: nil, author_id: nil, author_type: nil, title: nil, body: nil, created_at: nil, updated_at: nil>2.1.1 :012 > p.author= s => #<Student id: 1, name: "ananta", email: nil, created_at: "2015-01-03 07:06:51", updated_at: "2015-01-03 07:06:51">2.1.1 :013 > p.title="test title" => "test title"2.1.1 :014 > p.save2.1.1 :015 > Post.all [#<Post id: 1, author_id: 1, author_type: "Student", title: "test title", body: nil, created_at: "2015-01-03 07:08:02", updated_at: "2015-01-03 07:08:02">
2.1.1 :019 > t=Teacher.create(name:"dddd") => #<Teacher id: 1, name: "dddd", email: nil, office: nil, created_at: "2015-01-03 07:15:02", updated_at: "2015-01-
03 07:15:02">2.1.1 :020 > p=Post.create(author:t, title: "this is title", body:"this is body") => #<Post id: 2, author_id: 1, author_type: "Teacher", title: "this is title", body: "this is body", created_at: "2015-01-
03 07:16:31", updated_at: "2015-01-03 07:16:31">2.1.1 :023 > Post.all => #<ActiveRecord::Relation [#<Post id: 1, author_id: 1, author_type: "Student", title: "test title", body: nil, created_at: "2015-01-03 07:08:02",
updated_at: "2015-01-03 07:08:02">, #<Post id: 2, author_id: 2, author_type: "Teacher", title: "this is title", body: "this is body", created_at: "2015-01-
03 07:16:31", updated_at: "2015-01-03 07:16:31">]>2.1.1 :024 > p= Post.find(1) => #<Post id: 1, author_id: 1, author_type: "Student", title: "test title", body: nil, created_at: "2015-01-03 07:08:02",
updated_at: "2015-01-03 07:08:02">2.1.1 :025 > p.author.name => "ananta"
POlymorphic many to any association
Scopes
What is Scope?A scope is a subset of a collection.
ScenarioYou have Users. Now, some of those Users are subscribed to your newsletter. You marked those who receive a newsletter by adding a field to the Users
Database (user.subscribed_to_newsletter = true). Naturally, you sometimes want to get those Users who are subscribed to
your newsletter.ref:
http://stackoverflow.com/questions/4869994/what-is-scope-named-scope-in-rails
of course, we always do this:User.where(subscribed_to_newsletter: true)
Instead of always writing this you could, however, do something like this.#File: users.rb
class User < ActiveRecord::Base scope :newsletter, where(subscribed_to_newsletter: true)end
This allows you to access your subscribers by simply doing this:User.newsletter
Class methods on your model are automatically available on scopes. Assuming the following setup:class Article < ActiveRecord::Base scope :published, -> { where(published: true) } scope :featured, -> { where(featured: true) }
def self.latest_article order('published_at desc').first end
def self.titles pluck(:title) endend
We are able to call the methods like this:Article.published.featured.latest_articleArticle.featured.titles
default_scopehttp://rails-bestpractices.com/posts/806-
default_scope-is-evil
class Post default_scope where(published: true).order("created_at desc")end
> Post.limit(10) Post Load (3.3ms) SELECT `posts`.* FROM `posts` WHERE `posts`.`published` = 1 ORDER BY created_at desc LIMIT 10
> Post.unscoped.order("updated_at desc").limit(10) Post Load (1.9ms) SELECT `posts`.* FROM `posts` ORDER BY updated_at desc LIMIT 10
Thank you