20

What I mean is if I have two models, connected by a has_and_belongs_to_many association, can I store other data in the join table for each association? That is, the extra data would not be part of a single record in either table, but instead of the connection between them.

My actual models are as follows:

class Part < ActiveRecord::Base
  has_and_belongs_to_many :assemblies
  has_and_belongs_to_many :packages
  belongs_to :user

  validates :name, :user_id, :presence => true
end

class Package < ActiveRecord::Base
  has_and_belongs_to_many :parts
  belongs_to :user
end

So the point is that each part is available in many packages, and each package has different parts. What I want to add is a quantity. That would not be quantity of each part, but of each package of each part.

I can't find how to do this in ActiveRecord. If I was not using rails/activerecord, I'd just add a quantity column to the join table which relates parts to packages. I could obviously make this change in a migration, but how would I access the value using ActiveRecord?

Arslan Ali
  • 17,418
  • 8
  • 58
  • 76
David Robertson
  • 1,809
  • 3
  • 12
  • 12

3 Answers3

31

Short answer no you cannot with a HABTM relationship. It is only intended for simple many to many relationships.

You will need to use a has_many :through relationship. In this scenario you will create a join model (PartPackage) where you can define the extra attributes that you need.

class Part < ActiveRecord::Base
  has_many :part_packages
  has_many :packages, :through => :part_packages

  has_and_belongs_to_many :assemblies
  belongs_to :user

  validates :name, :user_id, :presence => true
end

class PartPackage < ActiveRecord::Base
  belongs_to :part
  belongs_to :package
end

class Package < ActiveRecord::Base
  has_many :part_packages
  has_many :parts, :through => :part_packages
  belongs_to :user
end
Andrew Cetinic
  • 2,805
  • 29
  • 44
16

There is a key difference between has_many :through and has_and_belongs_to_many the Rails guide explains the differences between the two options in greater detail, however if you want to add data that describes the relationship then use has_many :through and you can access the model that joins the two.

This is what has_many :through looks like: Credit to the Rails guide.

Devin M
  • 9,636
  • 2
  • 33
  • 46
  • 1
    What program did you use to make such a nice outline? – MicFin Jun 01 '16 at 19:21
  • (my internal ref #485 ) How do you update the appointment_date at the same time the whole record is created and also how do you read the extra attribute later on? I can only do it via Appointment.find_by............. but I would like something less wacky like patient.appointments.first.appointment_date – ace Oct 01 '16 at 11:59
  • You can update the appointment_date attribute like this: belongs_to :physician, :touch => :appointment_date. – hashrocket May 17 '17 at 18:12
  • @MicFin he takes it from ror guide – Mauro Jun 17 '20 at 14:40
4

Actually, this is possible.

1) Add a quantity key to the packages_parts join table

2) Add this to your Part model

has_and_belongs_to_many :packages, -> { select("packages.*, 
packages_parts.quantity") }

Now you can do part.packages[0].quantity, for example.

What this does is tell Rails to fetch the quantity key whenever it gets the packages for a specific part.

(You can do the same for your Package model if you want to do package.parts.first.quantity as well - just put this on the Package model also, replacing 'packages' with 'parts')

More info: https://ducktypelabs.com/using-scope-with-associations/

Shady
  • 131
  • 1
  • 3