Model Relationships

In Review

  • You now know how to implement all of CRUD!

Web Apps in 4 Steps

  1. Pick action (new, create, etc.)
  2. Route to action (usu. use resources)
  3. Write controller action
  4. Create view if necessary

Now you know!

What file gets touched second when you type in 'localhost:3000/pokemons'?

  1. app/views/pokemons/index.html.erb
  2. app/controllers/pokemons_controller.rb
  3. config/routes.rb
  4. app/models/pokemon.rb

How do I query for all the pokemon that are above level 30?

  1. Pokemon.where('level > 30')
  2. Pokemon.where(level: '> 30')
  3. Pokemon.find(30)
  4. @pokemon.level(30)

Assume the Pokemon table has the level column as an integer.

users
id name num_badges
1 Sam 0
2 Mark 8
pokemons
id name level description
1 Charmander 35 The best starter
4 Bulbasaur 25 The one no one chooses

How do I get the Pokemon that belongs to a User with name: "Sam" and id: 2?

  1. Pokemon.find(User.find(2))
  2. Pokemon.where(id: 2)
  3. Pokemon.where(User.name: 'Sam')
  4. This is impossible!
users
id name num_badges
1 Sam 0
2 Mark 8
pokemons
id name level description
1 Charmander 5 The best starter
4 Bulbasaur 2 The one no one chooses

Houston, we have a problem!

pokemons
id name level description
1 Charmander 5 The best starter
4 Bulbasaur 2 The one no one chooses
users
id name num_badges
1 Sam 0
2 Mark 8

How does a Pokemon know

which owner it belongs to?

Solution: a new column

pokemons
id name level description
1 Charmander 5 The best starter
4 Bulbasaur 2 The one no one chooses
users
id name num_badges
1 Sam 0
2 Mark 8
pokemons
id user_id name level description
1 1 Charmander 5 The best starter
4 2 Bulbasaur 2 The one no one chooses

aka "foreign key"

Key Idea: Model Associations

  • One model can belong to another
  • One model can have many of another
  • belongs_to in Pokemon
  • has_many in User

In Rails

  • Makes sense to ask a user for his/her pokemon:
  • @user.pokemons # [Pokemon(id: 2), ...]

 

  • Also makes sense to ask a pokemon for its user
  • @pokemon.user # User(id: 1)

 

  • Rails does this for us!

Checking the Docs

http://guides.rubyonrails.org/association_basics.html
  • You get a bunch of nifty methods just for including :has_many and :belongs_to!

 

  • @user.pokemons
  • @user.pokemons.build(...)
  • @user.pokemons.size

Which line errors?

@user = User.find(1)
@pokemon = Pokemon.find(1)
:belongs_to in Pokemon, :has_many in User
  1. @pokemon.user
  2. @user.pokemons
  3. @user.pokemons.size
  4. @user.pokemons.build(name: 'Ditto')
users
id name num_badges
1 Sam 0
2 Mark 8
pokemons
id name level description
1 Charmander 5 The best starter
4 Bulbasaur 2 The one no one chooses

What Did I Forget?

  • Don't forget to actually make the user_id column!

 

  • How?

Some caveats

  • Rails infers column from has_many declaration
  • What if you want to change that?

Some caveats

users
id name num_badges
1 Sam 0
2 Mark 8
pokemons
id trainer_id name level description
1 1 Charmander 5 The best starter
4 2 Bulbasaur 2 The one no one chooses

has_many :pocket_monsters, class: "Pokemon", foreign_id: "trainer_id"

What about:

users (trainers)
id name num_badges
1 Sam 0
2 Mark 8
gyms
id name num_badges
1 pewter city 0
2 veridian city 8

What we want

  • @trainers.gyms
  • @gym.trainers

has_many and belongs_to doesn't work!

users (trainers)
id name num_badges
1 Sam 0
2 Mark 8
gyms
id name num_badges
1 pewter city 0
2 veridian city 8

Intermediate table!

users (trainers)
id name num_badges
1 Sam 0
2 Mark 8
gyms
id name num_badges
1 pewter city 0
2 veridian city 8
user_gyms
id gym_id user_id
1 1 1
2 1 2

Rails: Has and belongs to many

has_many :user_gyms
has_many :gyms, through: :user_gyms
has_many :user_gyms
has_many :users, through: :user_gyms
belongs_to: user
belongs_to: gym

In user.rb

In gym.rb

In user_gyms.rb

Step 0

  • Initialize

Step 1

  • Migration

Step 2

Step 3

  • Model
  • Views

Demo!

Spring 2016 - Week 5: Model Relationships

By Rails Decal

Spring 2016 - Week 5: Model Relationships

  • 1,167