# Has\_many through but with one table?

**URL:** <https://www.ruby-forum.com/t/has-many-through-but-with-one-table/174130>\
**Category:** Rails\
**Created:** [August 21, 2009, 5:59am UTC](https://www.ruby-forum.com/t/has-many-through-but-with-one-table/174130 "2009-08-21T05:59:33Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![casper\_the\_ghost](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/c/58956e/32.png) [@casper\_the\_ghost](https://www.ruby-forum.com/u/casper_the_ghost)\
**Post date:** [August 21, 2009, 5:59am UTC](https://www.ruby-forum.com/t/has-many-through-but-with-one-table/174130/1 "2009-08-21T05:59:33Z")

</div>

Maybe I’m looking at this the wrong way and with that - hopefully  
someone can either suggest a simpler way or the right way.

I have a Quote model - for all intents and purposes, just say it has  
one attribute :price.

I want to be able to add the following self-referential attributes  
(which I _think_ should be under a has\_many, through)

Quotes  
has\_many requotes through… something

When a quote is made it’s attached to a specific job and locked. When  
a similar job comes in, I want to take the original quote, duplicate  
it and update it to match the specs of the new job; the old quote  
still intact and both having a knowledge of their “parent-child”  
relationship.

I’ve looked at Ryan B. latest railscast on self referential  
relationships but that doesn’t seem to solve the whole problem.

Say I have quote\_id 3 which has been requoted 3 different times. I  
then go back and requote one of those, say quote #4… (check the  
“hopefully understandable” chart below)

quote#3  
=quote#4  
==quote#7  
=quote#5  
=quote#6

quote 3 has 3 children and its first child has a child.

now say I delete quote 4… quote 7 doesn’t know who its proverbial  
daddy is. and quote 3 doesn’t know it has any grandchildren. in  
theory, i’d like 3&7 to link up when the link between them breaks.

any takers on this one?

---

<div class="post-metadata">

**Author:** ![casper\_the\_ghost](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/c/58956e/32.png) [@casper\_the\_ghost](https://www.ruby-forum.com/u/casper_the_ghost)\
**Post date:** [August 21, 2009, 7:39am UTC](https://www.ruby-forum.com/t/has-many-through-but-with-one-table/174130/2 "2009-08-21T07:39:39Z")

</div>

On Aug 20, 2009, at 11:58 PM, removed\_email\_address@domain.invalid wrote:

> has\_many requotes through… something  
> Say I have quote\_id 3 which has been requoted 3 different times. I
> 
> now say I delete quote 4… quote 7 doesn’t know who its proverbial  
> daddy is. and quote 3 doesn’t know it has any grandchildren. in  
> theory, i’d like 3&7 to link up when the link between them breaks.
> 
> any takers on this one?

Well, it’s true that #4 knows its original, but #3 only knows about  
the requotes by asking, in effect, “Hey, I’m #3. Which of you quotes  
are requotes from me?”

class Quote  
belongs\_to :original, :class\_name =\> ‘Quote’  
has\_many :requotes, :class\_name =\> ‘Quote’, :foreign\_key =\> :original

def requoted?  
!self.original.nil?  
end

def requote  
raise “Oops! Can’t requote an unsaved Quote” if self.new\_record?  
child = self.clone  
child.original = self.id  
end  
end

three = Quote.new(:price =\> 1.00) {|q| q.id = 3 }  
three.save  
four = three.requote; four.save  
five = three.requote; five.save  
six = three.requote; six.save  
seven = four.requote; seven.save

OK, now then, is four.destroy allowed? (meaning does the business  
thing exist in such a way that it is OK?)

You might want to have a before\_destroy callback that prevents a quote  
from being removed if it has been requoted.

class Quote  
before\_destroy :ensure\_no\_requotes # placed earlier than the  
has\_many

protected  
def ensure\_no\_requotes  
self.requotes.empty?  
end  
end

Alternatively, you could treat the chain of requotes as a linked-list  
and  
self.requotes.each {|r|r.original = self.original; r.save}  
in an after\_destroy callback perhaps.

-Rob

Rob B. [http://agileconsultingllc.com](http://agileconsultingllc.com)  
removed\_email\_address@domain.invalid
