Benjamin Crouzier
Benjamin Crouzier

Reputation: 41965

Has and belongs_to many or has_many through example with active record

Let's say that I have a fictional car rental application.

I have users, cars, and addresses.

user (id, name, email)
  has_many :addresses
  has_many :cars

car (id, name)
  belongs_to :user

address (id, name, user_id)
  belongs_to :user

Each user has multiple addresses and cars. An address is a location where the user can lend his car.

Now, with this model, I would like to do the following (Let's say user #1 has 2 addresses and 3 cars):

Settings for User #1 : 
(specify at which address each of your cars are available)
/-------------------------------------------\
|           |  Car #1  | Car #2  |  Car #3  |
|-----------+----------+---------+----------|
| Address 1 |   yes    |  yes    |    no    |
|-----------+----------+---------+----------|
| Address 2 |   no     |  no     |   yes    |
\-------------------------------------------/

I think to achieve that you could create a table cars_addresses (id, car_id, address_id, available:bool)

But I don't know how to specify that with active record. So my questions are:

Upvotes: 1

Views: 274

Answers (2)

anton
anton

Reputation: 11

It depends on what your 'car' model represents.

If it's a physical car that can't be at multiple locations at a time, then another relation will do:

car (id, name, location_id)
    belongs_to :user
    belongs_to :location

location (id, name, user_id)
    belongs_to :user
    has_many :cars

This will make relations work:

#list each car and it's location
current_user.cars.each do |car| { puts "#{car.name} is at #{car.location.name}" }  

or

#how many cars are at each location
current_user.locations.each do |location| { puts "#{location.cars.count} of your cars are at #{location.name}" }

To set a location for a car:

#move car with id=5 to location with id=15
c=Car.find(5)
l=Location.find(15)
c.location=l
c.save

or

c=Car.find(5)
c.location_id=15
c.save

BTW, I suggest to name your model 'location' instead of an 'address'. Ruby automagically generates a lot of methods with pluralized name of your model, so using words with simple pluralized form will help you avoid some confusion.

Upvotes: 1

BroiSatse
BroiSatse

Reputation: 44725

What you need is has_and_belongs_to_many between cars and address. However plenty of rubbyists would say that this relationship should never be used (and has_many :through should be used instead) this is a perfect place for it as I can't think of any additional information which would need to be stored between those models. So it would be:

user (id, name, email)
  has_many :addresses
  has_many :cars

car (id, name)
  belongs_to :user
  has_and_belongs_to_many :addresses

address (id, name, user_id)
  belongs_to :user
  has_and_belongs_to_many :cars

Then you need to create table addresses_cars (orders matters, no model needed) without id and with two collumns: address_id and car_id. That's it! It will "magically" work:

user.cars.first.addresses => list of location car is available
user.addresses.first.cars => list of cars available under address
user.addresses.first.cars << user.cars.first => add a car to given address

Upvotes: 2

Related Questions