Cigol
Cigol

Reputation: 105

Rails, Product model containing one or more Part item model references

Concerning a Product model that houses multiple Part items;

...for example;

Product (Car)
 Part (Engine) x1
 part (Wheel) x4
 part (Chassis) x1

I'm trying to wrap my head around setting this up and some help would be appreciated. In my Product model I'm assuming I set up an:

has_many :parts

When I do this though I can only assign one part to each product and I can't specify a numerical value? How would you approach this problem?

Upvotes: 0

Views: 162

Answers (2)

max
max

Reputation: 102443

To create a many to many assocation between Product and Part you need a join table:

class Product < ApplicationRecord
  has_many :product_parts
  has_many :parts, through: :product_parts
end

class Part < ApplicationRecord
  has_many :product_parts
  has_many :products, through: :product_parts
end

class ProductPart < ApplicationRecord
  belongs_to :product
  belongs_to :part
end

Here product_parts serves as the join table. You could for example attach four wheels to a car by:

car = Product.create(name: 'Car')
wheel = Part.create(name: 'Wheel')
4.times { car.parts << wheel }
car.parts.count # => 4
car.parts
# => #<ActiveRecord::Associations::CollectionProxy [#<Part id: 1, name: "Wheel", created_at: "2018-12-06 15:12:18", updated_at: "2018-12-06 15:12:18">, #<Part id: 1, name: "Wheel", created_at: "2018-12-06 15:12:18", updated_at: "2018-12-06 15:12:18">, #<Part id: 1, name: "Wheel", created_at: "2018-12-06 15:12:18", updated_at: "2018-12-06 15:12:18">, #<Part id: 1, name: "Wheel", created_at: "2018-12-06 15:12:18", updated_at: "2018-12-06 15:12:18">]>

This would create 4 rows in the product_parts table. Another way to solve this would be by adding a quantity column to the product_parts table.

car.product_parts.create(part: wheel, quantity: 4)

Adding "metadata" to the join model is only possible when its created explicitly. Note that this won't actually alter the count and length of the association like in the previous example.

car.parts.count # => 1 
car.parts
# => #<ActiveRecord::Associations::CollectionProxy [#<Part id: 1, name: "Wheel", created_at: "2018-12-06 15:12:18", updated_at: "2018-12-06 15:12:18">]>

Rather you have to use the join table records to get the quantity:

<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Quantity</th>
    </tr>
  </thead>
  <tbody>
    <% @product.product_parts.each do |pp| %>
    <tr>
      <td><%= pp.part.name %></td>
      <td><%= pp.quanty %></td>
    </tr>
    <% end %>
  </tbody>
</table>

Upvotes: 1

Mark
Mark

Reputation: 6455

In your case, a car has many parts and a part belongs to one car. This can be setup as follows:

class Car < ApplicationRecord
  has_many :parts
end

class Part < ApplicationRecord
  belongs_to :car
end

This assumes that the Part model has a column called car_id, which is used to specify which car the part belongs to. You'll then be able to do:

car.parts
part.car

EDIT:

Say we have 5 parts:

part1 = Part.find(1)
part2 = Part.find(2)
part3 = Part.find(3)
part4 = Part.find(4)
part5 = Part.find(5)

And one product:

product1 = Product.find(1)

If I want to assign the parts to belong to the product, I would do:

part1.product_id = product1.id
part1.save

I can do that to all 5 parts, which will assign them all to the parent product.

If you want to see which parts belong to the product, you can do:

product1.parts

which will return

[part1, part2, part3, part4, part5]

As well as

part1.product

Which will return product1

Upvotes: 2

Related Questions