has_many :through

This seems like it should have been asked numerous times already, but my
Google-fu isn't strong enough to find the answer. DHH advocates
separating HABTM relationships into a join model, and I need to store
some metadata about the join, so this makes sense. The problem is
creation order and validations.

Say, you have three models, like so:

class Foo < AR::Base
  has_many :bars, :through => :quux
  has_many :quuxes
end

class Bar < AR::Base
  has_many :quuxes, :dependent => :destroy
  has_many :foos, :through => :quuxes
end

class Quux < AR::Base
  belongs_to :foo
  belongs_to :bar
end

Foo is valid without any Bars, and a Bar may belong to multiple Foos.
However, a Bar should never exist without being associated with at least
one Foo.

So, how would you ensure that every time a Bar is created, the proper
Quux is also created, without ever leaving an opportunity for a Bar to
exist without a Foo?

My initial thought is that a transaction is necessary, in this order:
1) Create Bar (rollback on fail)
2) Create Quux, referencing bar.id and foo.id (rollback on fail)

How do I enforce this order and maintain database integrity in the model
layer?

This seems silly; if there's an obvious answer/resource I'm missing,
please let me know. Thanks.

Brandon Mitchell wrote:

This seems like it should have been asked numerous times already, but my
Google-fu isn't strong enough to find the answer. DHH advocates
separating HABTM relationships into a join model, and I need to store
some metadata about the join, so this makes sense. The problem is
creation order and validations.

Say, you have three models, like so:

class Foo < AR::Base
  has_many :bars, :through => :quux
  has_many :quuxes
end

class Bar < AR::Base
  has_many :quuxes, :dependent => :destroy
  has_many :foos, :through => :quuxes
end

class Quux < AR::Base
  belongs_to :foo
  belongs_to :bar
end

Foo is valid without any Bars, and a Bar may belong to multiple Foos.
However, a Bar should never exist without being associated with at least
one Foo.

So, how would you ensure that every time a Bar is created, the proper
Quux is also created, without ever leaving an opportunity for a Bar to
exist without a Foo?

My initial thought is that a transaction is necessary, in this order:
1) Create Bar (rollback on fail)
2) Create Quux, referencing bar.id and foo.id (rollback on fail)

How do I enforce this order and maintain database integrity in the model
layer?

This seems silly; if there's an obvious answer/resource I'm missing,
please let me know. Thanks.
--
Posted via http://www.ruby-forum.com/.

This is quite simple with validations. In your Bar model you will want
the following:

#Validate belongs to a foo
validates_presence_of :foo_id
#Validates foo exists - ONLY use this if the foo object is already
saved.
validates_associated :foos

Rails will do all the work with Quux on your behalf.

> #Validate belongs to a foo
> validates_presence_of :foo_id
This is, perhaps, what I would *like* to do, but Bar does not have a
foo_id. At least, Rails 2.0.2 doesn't seem to be proxying #foo_id on Bar
to #foo_id on Quux, by default.

Apologies, that should be :foo_ids (plural, as it is a has_many
association) and it should be managed directly by the association. It
is an array of the Foo id's associated with Bar. Updating this should
be updating the quuxes table according to the associations you have
set up.