Skip to content

Instantly share code, notes, and snippets.

@k00ka
Created April 16, 2015 14:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save k00ka/2cb1128f90659676fce3 to your computer and use it in GitHub Desktop.
Save k00ka/2cb1128f90659676fce3 to your computer and use it in GitHub Desktop.
W4D2 Lighthouse PT Toronto - ActiveRecord Associations
class Movie < ActiveRecord::Base
has_many :reviews
has_many :users, through: :reviews
end
# create an in-memory Movie instance (and throw it away, oops)
2.1.3 :001 > Movie.new
=> #<Movie id: nil, created_at: nil, updated_at: nil>
# create a persisted Movie object (also capture it in the local variable m)
2.1.3 :002 > m=Movie.create
(0.2ms) begin transaction
SQL (4.7ms) INSERT INTO "movies" ("created_at", "updated_at") VALUES (?, ?) [["created_at", "2015-04-16 01:09:35.617271"], ["updated_at", "2015-04-16 01:09:35.617271"]]
(3.2ms) commit transaction
=> #<Movie id: 1, created_at: "2015-04-16 01:09:35", updated_at: "2015-04-16 01:09:35">
# create a persisted User object (capture it in local variable u)
2.1.3 :003 > u=User.create
(0.0ms) begin transaction
SQL (2.9ms) INSERT INTO "users" ("created_at", "updated_at") VALUES (?, ?) [["created_at", "2015-04-16 01:10:33.593170"], ["updated_at", "2015-04-16 01:10:33.593170"]]
(2.5ms) commit transaction
=> #<User id: 1, created_at: "2015-04-16 01:10:33", updated_at: "2015-04-16 01:10:33">
# create a persisted review but fail to capture it (note the id in the output to use in the next step)
2.1.3 :004 > Review.create
(0.1ms) begin transaction
SQL (4.5ms) INSERT INTO "reviews" ("created_at", "updated_at") VALUES (?, ?) [["created_at", "2015-04-16 01:10:46.450161"], ["updated_at", "2015-04-16 01:10:46.450161"]]
(3.5ms) commit transaction
=> #<Review id: 1, created_at: "2015-04-16 01:10:46", updated_at: "2015-04-16 01:10:46", user_id: nil, movie_id: nil>
# retrieve the persisted Review object from the database
2.1.3 :005 > r=Review.find(1)
Review Load (1.5ms) SELECT "reviews".* FROM "reviews" WHERE "reviews"."id" = ? LIMIT 1 [["id", 1]]
=> #<Review id: 1, created_at: "2015-04-16 01:10:46", updated_at: "2015-04-16 01:10:46", user_id: nil, movie_id: nil>
# ** USING THE AUTO-GENERATED (by ActiveRecord) ASSOCIATION ACCESSOR METHODS TO MODIFY ASSOCIATIONS **
# set the review's user to the instance in the variable u
2.1.3 :006 > r.user = u
=> #<User id: 1, created_at: "2015-04-16 01:10:33", updated_at: "2015-04-16 01:10:33">
# set the reviews's movie to the instance in the variable m
2.1.3 :007 > r.movie = m
=> #<Movie id: 1, created_at: "2015-04-16 01:09:35", updated_at: "2015-04-16 01:09:35">
# a complete Review! But it's not saved yet.
2.1.3 :008 > r
=> #<Review id: 1, created_at: "2015-04-16 01:10:46", updated_at: "2015-04-16 01:10:46", user_id: 1, movie_id: 1>
2.1.3 :009 > r.save
(0.4ms) begin transaction
SQL (3.5ms) UPDATE "reviews" SET "movie_id" = ?, "updated_at" = ?, "user_id" = ? WHERE "reviews"."id" = 1 [["movie_id", 1], ["updated_at", "2015-04-16 01:11:44.023846"], ["user_id", 1]]
(3.0ms) commit transaction
=> true
# here's our movie
2.1.3 :010 > m
=> #<Movie id: 1, created_at: "2015-04-16 01:09:35", updated_at: "2015-04-16 01:09:35">
# traverse the association to find it's review...
2.1.3 :011 > m.review
NoMethodError: undefined method `review' for #<Movie:0xc0d413c>
# acutally, it's reviews (one-to-many)
2.1.3 :012 > m.reviews
Review Load (0.9ms) SELECT "reviews".* FROM "reviews" WHERE "reviews"."movie_id" = ? [["movie_id", 1]]
=> #<ActiveRecord::Associations::CollectionProxy [#<Review id: 1, created_at: "2015-04-16 01:10:46", updated_at: "2015-04-16 01:11:44", user_id: 1, movie_id: 1>]>
# now traverse the has_many through relationship...
2.1.3 :013 > m.users
User Load (0.9ms) SELECT "users".* FROM "users" INNER JOIN "reviews" ON "users"."id" = "reviews"."user_id" WHERE "reviews"."movie_id" = ? [["movie_id", 1]]
=> #<ActiveRecord::Associations::CollectionProxy [#<User id: 1, created_at: "2015-04-16 01:10:33", updated_at: "2015-04-16 01:10:33">]>
# similarly traverse the associations from the User side...
2.1.3 :014 > u.reviews
Review Load (1.5ms) SELECT "reviews".* FROM "reviews" WHERE "reviews"."user_id" = ? [["user_id", 1]]
=> #<ActiveRecord::Associations::CollectionProxy [#<Review id: 1, created_at: "2015-04-16 01:10:46", updated_at: "2015-04-16 01:11:44", user_id: 1, movie_id: 1>]>
2.1.3 :015 > u.movies
Movie Load (1.6ms) SELECT "movies".* FROM "movies" INNER JOIN "reviews" ON "movies"."id" = "reviews"."movie_id" WHERE "reviews"."user_id" = ? [["user_id", 1]]
=> #<ActiveRecord::Associations::CollectionProxy [#<Movie id: 1, created_at: "2015-04-16 01:09:35", updated_at: "2015-04-16 01:09:35">]>
# answer the question: how many movies has this user reviewed?
# this is the "hard" way since it uses the more complicated has_many through association
2.1.3 :016 > u.movies.count
(1.6ms) SELECT COUNT(*) FROM "movies" INNER JOIN "reviews" ON "movies"."id" = "reviews"."movie_id" WHERE "reviews"."user_id" = ? [["user_id", 1]]
=> 1
# using the knowledge that a User can only review each Movie once, this will always return the same result with less work
2.1.3 :017 > u.reviews.count
(1.3ms) SELECT COUNT(*) FROM "reviews" WHERE "reviews"."user_id" = ? [["user_id", 1]]
=> 1
# from the review, we can now get to the movie without hitting the database!
2.1.3 :021 > r.movie
=> #<Movie id: 1, created_at: "2015-04-16 01:09:35", updated_at: "2015-04-16 01:09:35">
# create some new objects... a new User and Movie
2.1.3 :022 > u2=User.create
(0.1ms) begin transaction
SQL (3.2ms) INSERT INTO "users" ("created_at", "updated_at") VALUES (?, ?) [["created_at", "2015-04-16 01:14:40.519086"], ["updated_at", "2015-04-16 01:14:40.519086"]]
(2.2ms) commit transaction
=> #<User id: 2, created_at: "2015-04-16 01:14:40", updated_at: "2015-04-16 01:14:40">
2.1.3 :023 > m2=Movie.create
(0.1ms) begin transaction
SQL (4.2ms) INSERT INTO "movies" ("created_at", "updated_at") VALUES (?, ?) [["created_at", "2015-04-16 01:14:48.565140"], ["updated_at", "2015-04-16 01:14:48.565140"]]
(2.8ms) commit transaction
=> #<Movie id: 2, created_at: "2015-04-16 01:14:48", updated_at: "2015-04-16 01:14:48">
# create a review for the second user, but for the first movie
2.1.3 :024 > r2=Review.create(user: u2, movie: m)
(0.1ms) begin transaction
SQL (3.4ms) INSERT INTO "reviews" ("created_at", "movie_id", "updated_at", "user_id") VALUES (?, ?, ?, ?) [["created_at", "2015-04-16 01:15:17.359864"], ["movie_id", 1], ["updated_at", "2015-04-16 01:15:17.359864"], ["user_id", 2]]
(3.6ms) commit transaction
=> #<Review id: 2, created_at: "2015-04-16 01:15:17", updated_at: "2015-04-16 01:15:17", user_id: 2, movie_id: 1>
# answer the question: how many reviews are there for the first Movie (the one stored in m)
2.1.3 :027 > m.reviews.count
(1.9ms) SELECT COUNT(*) FROM "reviews" WHERE "reviews"."movie_id" = ? [["movie_id", 1]]
=> 2
# answer the question: which users have reviewed the first Movie (the one stored in m)
2.1.3 :028 > m.users
=> #<ActiveRecord::Associations::CollectionProxy [#<User id: 1, created_at: "2015-04-16 01:10:33", updated_at: "2015-04-16 01:10:33">]>
# answer the question: how many users have reviewed the first Movie (note: same as number of reviews)
2.1.3 :029 > m.users.count
(2.1ms) SELECT COUNT(*) FROM "users" INNER JOIN "reviews" ON "users"."id" = "reviews"."user_id" WHERE "reviews"."movie_id" = ? [["movie_id", 1]]
=> 2
# answer the question: which users have reviewed the second Movie (the one stored in the variable m2)
2.1.3 :036 > m2.users
User Load (1.3ms) SELECT "users".* FROM "users" INNER JOIN "reviews" ON "users"."id" = "reviews"."user_id" WHERE "reviews"."movie_id" = ? [["movie_id", 2]]
=> #<ActiveRecord::Associations::CollectionProxy []>
# answer the question: how many users have reviewed the second movie (same as the number of reviews, just harder to compute)
2.1.3 :037 > m2.users.count
(2.5ms) SELECT COUNT(*) FROM "users" INNER JOIN "reviews" ON "users"."id" = "reviews"."user_id" WHERE "reviews"."movie_id" = ? [["movie_id", 2]]
=> 0
# retrieve the reviews for the second Movie (there are none!)
2.1.3 :038 > m2.reviews
Review Load (1.8ms) SELECT "reviews".* FROM "reviews" WHERE "reviews"."movie_id" = ? [["movie_id", 2]]
=> #<ActiveRecord::Associations::CollectionProxy []>
class Review < ActiveRecord::Base
belongs_to :movie
belongs_to :user
end
class User < ActiveRecord::Base
has_many :reviews
has_many :movies, through: :reviews
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment