Patrick Doyle
2009-Sep-25 17:38 UTC
How to ensure two tables reference the same record in a third table?
class Part < ActiveRecord::Base has_many :lots has_many :components # or, maybe not end class Lot < ActiveRecord::Base belongs_to :part has_many :components end class Component < ActiveRecord::Base belongs_to :part # or, maybe not belongs_to :lot end In my domain, a "lot" of "components" has a part number (which I represent in RoR as a #belongs_to relationship, even though the term "belongs_to" is somewhat misleading). Each component in the lot has a part number which is the same as the part number for the entire lot. What is the best way to ensure that this consistency is maintained? That is, that all parts in a lot reference the same part number as the lot itself. One way to ensure the consistency is to remove the "part_id" column from the component table and to reference a components part number through the lot association, i.e. mycomponent.lot.part.number. The problem is, sometimes I want to look at all of the parts that have a given part number, and the only way I can see to do that is to collect all of the lots with a given part number and then collect all of the components in each of the lots, using something like: mypart.lots.map(&:components).flatten. Unfortunately, that returns an array of components instead of the dynamic finder that would be returned by mypart.components. A second way to ensure the consistency is to add validations to the Lot and/or Component models. I could do that, but I have been advised elsewhere that validations are best when augmented by database constraints. (See http://groups.google.com/group/rubyonrails-talk/browse_thread/thread/3b023e81df58619d, if you are interested). Is there some database agnostic way to add this sort of constraint? Or is there some other mechanism to implement this? --wpd
Marnen Laibow-Koser
2009-Sep-25 17:55 UTC
Re: How to ensure two tables reference the same record in a
Patrick Doyle wrote:> class Part < ActiveRecord::Base > has_many :lots > has_many :components # or, maybe not > end > > class Lot < ActiveRecord::Base > belongs_to :part > has_many :components > end > > class Component < ActiveRecord::Base > belongs_to :part # or, maybe not > belongs_to :lot > end > > In my domain, a "lot" of "components" has a part number (which I > represent in RoR as a #belongs_to relationship, even though the term > "belongs_to" is somewhat misleading). Each component in the lot has a > part number which is the same as the part number for the entire lotThen the part number is a property of the lot, not the component.> > What is the best way to ensure that this consistency is maintained? > That is, that all parts in a lot reference the same part number as the > lot itself.Model the data correctly. Represent the part number once and only once, presumably in the lot.> > One way to ensure the consistency is to remove the "part_id" column > from the component table and to reference a components part number > through the lot association, i.e. mycomponent.lot.part.number.That is the correct way to do it. Repeating data in the database is always a sign that something is wrong with your schema.> The > problem is, sometimes I want to look at all of the parts that have a > given part number, and the only way I can see to do that is to collect > all of the lots with a given part number and then collect all of the > components in each of the lots, using something like: > mypart.lots.map(&:components).flatten.Part has_many :components, :through => lots mypart.components Done! [...]> --wpdBest, -- Marnen Laibow-Koser http://www.marnen.org marnen-sbuyVjPbboAdnm+yROfE0A@public.gmane.org -- Posted via http://www.ruby-forum.com/.
Patrick Doyle
2009-Sep-25 17:59 UTC
Re: How to ensure two tables reference the same record in a
On Fri, Sep 25, 2009 at 1:55 PM, Marnen Laibow-Koser <rails-mailing-list-ARtvInVfO7ksV2N9l4h3zg@public.gmane.org> wrote:> Part has_many :components, :through => lots > > mypart.components > > Done!Light dawns on marble head. Of course! Sorry for the noise. --wpd
Patrick Doyle
2009-Sep-25 18:05 UTC
Re: How to ensure two tables reference the same record in a
On Fri, Sep 25, 2009 at 1:59 PM, Patrick Doyle <wpdster-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:> On Fri, Sep 25, 2009 at 1:55 PM, Marnen Laibow-Koser > <rails-mailing-list-ARtvInVfO7ksV2N9l4h3zg@public.gmane.org> wrote: >> Part has_many :components, :through => lots >> >> mypart.components >> >> Done! > > Light dawns on marble head. > > Of course! Sorry for the noise.oh yeah, and thanks! --wpd
Patrick Doyle
2009-Sep-25 18:39 UTC
Re: How to ensure two tables reference the same record in a
Wow, so now I can do: class Component < ActiveRecord::Base # Make component.part a shortcut for component.lot.part def part(*args) lot.send :part, args end end ...and do things like: <%= mycomponent.part.number %> ...instead of ... <%= mycomponent.lot.part.number %> nice! --wpd
delegate does precisely this..checkout http://api.rubyonrails.org/classes/Module.html#M000110 On Sep 25, 11:39 pm, Patrick Doyle <wpds...-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:> Wow, so now I can do: > > class Component < ActiveRecord::Base > # Make component.part a shortcut for component.lot.part > def part(*args) > lot.send :part, args > end > end > > ...and do things like: > > <%= mycomponent.part.number %> > > ...instead of ... > > <%= mycomponent.lot.part.number %> > > nice! > > --wpd
Marnen Laibow-Koser
2009-Sep-28 10:55 UTC
Re: How to ensure two tables reference the same record in a
Patrick Doyle wrote:> Wow, so now I can do: > > class Component < ActiveRecord::Base > # Make component.part a shortcut for component.lot.part > def part(*args) > lot.send :part, argsNo need for send here: def part(*args, &block) lot.part(*args, block) # may need an if block_given? end> end > end > > ...and do things like: > > <%= mycomponent.part.number %> > > ...instead of ... > > <%= mycomponent.lot.part.number %> > > nice!Yup. Of course, you could have done that even without the :through.> > --wpdBest, -- Marnen Laibow-Koser http://www.marnen.org marnen-sbuyVjPbboAdnm+yROfE0A@public.gmane.org -- Posted via http://www.ruby-forum.com/.
Patrick Doyle
2009-Sep-28 18:43 UTC
Re: How to ensure two tables reference the same record in a
> No need for send here: > > def part(*args, &block) > lot.part(*args, block) # may need an if block_given? > end >I was thinking that I would have to use #send since there is so much Ruby magic wrapped inside ActiveRecord in order to map column names to object attributes, but a very simple test (following your email) showed me I was wrong. (And it seems to work fine w/o block_given? -- I can''t find any ActiveRecord instance methods that take a block, so this might be overkill anyway.) Basically, I wanted to add something like belongs_to :part, :through => :lot to my Component model, which I was happy to see I could do with 3 lines of code. I just picked 3 lines of code that were more complicated than they needed to be.> Yup. Of course, you could have done that even without the :through. >I want to relationship to go both ways, and as far as I can tell, there is no belongs_to :through => construct similar to the has_many :through => construct. But it is easy enough to implement with the single method definition. Thanks again for the help and the tips. I appreciate them both. --wpd