# Has\_many through question

**URL:** <https://www.ruby-forum.com/t/has-many-through-question/128935>\
**Category:** Rails\
**Created:** [February 12, 2008, 8:00pm UTC](https://www.ruby-forum.com/t/has-many-through-question/128935 "2008-02-12T20:00:12Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![daniel304](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/e5b9ba/32.png) [@daniel304](https://www.ruby-forum.com/u/daniel304)\
**Post date:** [February 12, 2008, 8:00pm UTC](https://www.ruby-forum.com/t/has-many-through-question/128935/1 "2008-02-12T20:00:12Z")

</div>

I have made 3 tables

±-----------+ ±---------------+ ±-----------+  
|people | |people\_campaigns| |campaigns |  
±-----------+ ±---------------+ ±-----------+  
|id :int | \<----\> |people\_id :int | /-\> |id :int |  
|name :string| |campaign\_id :int| \<-/ |name :string|  
|etc … | |exported :int | |desc :string|  
±-----------+ ±---------------+ ±-----------+

The people model looks like this

has\_many :people\_campaigns  
has\_many :campaigns, :through =\> :people\_campaigns

the other models I left empty because there is no need for me to work  
the other way I think

What I want to do is make an export of all people that are in campaign  
“x” and are not yet exported. If they are exported then update with  
people\_campaigns.exported=1

what I did is (except for the fastercsv lines):

people = Person.find(:all)  
people.each do |person|  
campaigns = person.campaign  
campaings.each do |campaign|  
if campaign.name == params[:campaignName]  
tester = PeopleCampaign.find(:first,  
:conditions =\> [“person\_id=? and  
campaign\_id=? and isnull(exported)”,person.id,campaign.id]  
)  
if tester  
#print all to fastercsvlines  
tester.exported = 1  
tester.save  
end  
end  
end  
end

This part is out of the top of my head but I don’t like this code and I  
think there is a nicer and better (ruby on rails) way to do this ???  
especially the part where i lookup the peoplecampaign record for which i  
had to add an id (yeah didn’t mention it in the table layout)

---

<div class="post-metadata">

**Author:** ![daniel304](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/e5b9ba/32.png) [@daniel304](https://www.ruby-forum.com/u/daniel304)\
**Post date:** [February 13, 2008, 5:59am UTC](https://www.ruby-forum.com/t/has-many-through-question/128935/2 "2008-02-13T05:59:46Z")

</div>

On Feb 12, 11:00 am, Daniel Van den oord \<rails-mailing-l…@andreas-  
[s.net](http://s.net)\> wrote:

> The people model looks like this
> 
> has\_many :people\_campaigns  
> has\_many :campaigns, :through =\> :people\_campaigns

One thing which I notice is that you have two options:

has\_many :campaigns, :through =\> relationships

(choose a better model name than “relationship”)

Or:

has\_and\_belongs to :campaigns\_people

with campaigns first.

At least, that’s my understanding. I don’t think that there’s  
anything technically wrong for a hmt relationship to use  
“campaigns\_people”, but it’s not the convention.

-Thufir

---

<div class="post-metadata">

**Author:** ![daniel304](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/e5b9ba/32.png) [@daniel304](https://www.ruby-forum.com/u/daniel304)\
**Post date:** [February 13, 2008, 7:40am UTC](https://www.ruby-forum.com/t/has-many-through-question/128935/3 "2008-02-13T07:40:49Z")

</div>

My main problem is with the part that it loops twice and does a seperate  
lookup. I am worried that this had major performance problems because  
the Person table will hold 1.2 million records and at least 700.000  
people per campaigns there will be 4 at least this means that the  
people\_campaigns table will grow to about 2.8 million records and  
perhaps more

The naming convention I got from the rails wiki but I will look into  
this however this isn’t really the problem

---

<div class="post-metadata">

**Author:** ![daniel304](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/e5b9ba/32.png) [@daniel304](https://www.ruby-forum.com/u/daniel304)\
**Post date:** [February 13, 2008, 4:13pm UTC](https://www.ruby-forum.com/t/has-many-through-question/128935/4 "2008-02-13T16:13:47Z")

</div>

On 2/13/08, Daniel Van den oord [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid)  
wrote:

> My main problem is with the part that it loops twice and does a seperate  
> lookup. I am worried that this had major performance problems because  
> the Person table will hold 1.2 million records and at least 700.000  
> people per campaigns there will be 4 at least this means that the  
> people\_campaigns table will grow to about 2.8 million records and  
> perhaps more
> 
> The naming convention I got from the rails wiki but I will look into  
> this however this isn’t really the problem

Let’s set this up conventionally:

class Person \< ActiveRecord::Base  
has\_many :campaigns :through =\> :campaign\_enrollments  
has\_many :campaign\_enrollments

# The people table needs at least id as well as any other attributes.

end

class CampaignEnrollment  
belongs\_to :person  
belongs\_to :campaign

# The campaign\_enrollments table needs at least id, person\_id,

campaign\_id and exported fields.  
end

class Campaign  
has\_many :campaign\_enrollments  
has\_many :people :through =\> :campaign\_enrollments

# the campaigns table needs at least id field plus other campaign

attributes.  
end

Now given a campaign

campaign = Campaign.find(…)

## campaign.campaign\_enrollments.find(:all, :include =\> [:people, ;campaigns] :conditions =\> [“exported !=?”, true]).each do | campaign\_enrollment| # whatever exporting does here campaign\_enrollment.update\_attribute(:exported, true) end

Rick DeNatale

My blog on Ruby  
[http://talklikeaduck.denhaven2.com/](http://talklikeaduck.denhaven2.com/)

---

<div class="post-metadata">

**Author:** ![daniel304](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/e5b9ba/32.png) [@daniel304](https://www.ruby-forum.com/u/daniel304)\
**Post date:** [February 14, 2008, 4:05pm UTC](https://www.ruby-forum.com/t/has-many-through-question/128935/5 "2008-02-14T16:05:06Z")

</div>

If I understood it correctly…  
almost eveything that can be in the controller can be done in the view  
(with some exceptions)

however this example is being used for an import and export  
the export doesn’t have a view I use fastercsv to generate a csv file  
streaming to the browser…

Thufir wrote:

> On Feb 13, 3:13 pm, “Rick DeNatale” [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:  
> […]
> 
> > campaign.campaign\_enrollments.find(:all, :include =\> [:people,  
> > ;campaigns] :conditions =\> [“exported !=?”, true]).each do |  
> > campaign\_enrollment|  
> > # whatever exporting does here  
> > campaign\_enrollment.update\_attribute(:exported, true)  
> > end  
> > […]
> 
> This sort of iteration couldn’t go in the controller, could it? It  
> would have to go in the view?
> 
> -Thufir

---

<div class="post-metadata">

**Author:** ![daniel304](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/e5b9ba/32.png) [@daniel304](https://www.ruby-forum.com/u/daniel304)\
**Post date:** [February 14, 2008, 3:57pm UTC](https://www.ruby-forum.com/t/has-many-through-question/128935/6 "2008-02-14T15:57:09Z")

</div>

On Feb 13, 3:13 pm, “Rick DeNatale” [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:  
[…]

> campaign.campaign\_enrollments.find(:all, :include =\> [:people,  
> ;campaigns] :conditions =\> [“exported !=?”, true]).each do |  
> campaign\_enrollment|  
> # whatever exporting does here  
> campaign\_enrollment.update\_attribute(:exported, true)  
> end  
> […]

This sort of iteration couldn’t go in the controller, could it? It  
would have to go in the view?

-Thufir

---

<div class="post-metadata">

**Author:** ![daniel304](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/e5b9ba/32.png) [@daniel304](https://www.ruby-forum.com/u/daniel304)\
**Post date:** [February 14, 2008, 4:16pm UTC](https://www.ruby-forum.com/t/has-many-through-question/128935/7 "2008-02-14T16:16:08Z")

</div>

On 13 Feb 2008, at 15:13, Rick DeNatale wrote:

> > perhaps more
> 
> # The people table needs at least id as well as any other
> 
> class Campaign  
> campaign.campaign\_enrollments.find(:all, :include =\> [:people,  
> ;campaigns] :conditions =\> [“exported !=?”, true]).each do |  
> campaign\_enrollment|  
> # whatever exporting does here  
> campaign\_enrollment.update\_attribute(:exported, true)  
> end

it might be nice to add

has\_many :unexported\_enrollments, :conditions =\> [“exported != ?”,  
true], :class\_name =\> “CampaignEnrollment”  
to campaign.

you could then write

campaign.unexported\_enrollments.find :all, :include =\> […] do |  
campaign\_enrollment|  
…  
end

This will do exactly the same thing, but can make things so much more  
readable.

Fred

---

<div class="post-metadata">

**Author:** ![daniel304](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/e5b9ba/32.png) [@daniel304](https://www.ruby-forum.com/u/daniel304)\
**Post date:** [February 14, 2008, 5:21pm UTC](https://www.ruby-forum.com/t/has-many-through-question/128935/8 "2008-02-14T17:21:33Z")

</div>

On Thu, Feb 14, 2008 at 10:05 AM, Daniel Van den oord  
[removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> If I understood it correctly…  
> almost eveything that can be in the controller can be done in the view  
> (with some exceptions)

But it really should be done in the model, with a method used to allow  
the controller to trigger it.

Putting too much business logic in a controller is a bad code smell.

Putting too much logic in views is an even bigger bad code smell.

## [Buckblog: Skinny Controller, Fat Model](http://weblog.jamisbuck.org/2006/10/18/skinny-controller-fat-model)

Rick DeNatale

My blog on Ruby  
[http://talklikeaduck.denhaven2.com/](http://talklikeaduck.denhaven2.com/)

---

<div class="post-metadata">

**Author:** ![daniel304](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/e5b9ba/32.png) [@daniel304](https://www.ruby-forum.com/u/daniel304)\
**Post date:** [February 14, 2008, 5:29pm UTC](https://www.ruby-forum.com/t/has-many-through-question/128935/9 "2008-02-14T17:29:21Z")

</div>

On Thu, Feb 14, 2008 at 10:15 AM, Frederick C.  
[removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> it might be nice to add  
> has\_many :unexported\_enrollments, :conditions =\> [“exported != ?”,  
> true], :class\_name =\> “CampaignEnrollment”  
> to campaign.

or even

has\_many :enrollments, :class\_name =\> “CampaignEnrollment” do  
def unexported  
find(:all, :conditions =\> [“exported !=\>”, true]  
end

Which would allow

campaign.enrollments.unexported.each do | enrollment |  
…

has\_many :enrollments

> > My blog on Ruby  
> > [http://talklikeaduck.denhaven2.com/](http://talklikeaduck.denhaven2.com/)
> > 
> > >
> 
> >

–  
Rick DeNatale

My blog on Ruby  
[http://talklikeaduck.denhaven2.com/](http://talklikeaduck.denhaven2.com/)
