Devigner blog about development and design

Has_many :through & Lookup Tables

Rails 1.0 is great, but 1.1 additions have made web dev even easier. One of the nice new features of 1.1 is has_many :through. A nice tutorial of hm:t can be found at blog.hasmanythrough.com (Part 1 & Part 2).

What i would like to cover is how to use a lookup (association/relationship) table on top of the hm:t feature.

For example, I have a table of `users` and a table of `reports`. Since multiple users can contribute to the report, we add a third table `contributions`.

class User &lt; ActiveRecord::Base <br/> &#xA0;&#xA0;has_many :contributions, :dependent =&gt; true<br/> &#xA0;&#xA0;has_many :reports, :through =&gt; :contributions <br/> end <br/><br/> class Report &lt; ActiveRecord::Base <br/> &#xA0;&#xA0;has_many :contributions, :dependent =&gt; true<br/> &#xA0;&#xA0;has_many :users, :through =&gt; :contributions <br/> end <br/><br/> class Contributions &lt; ActiveRecord::Base <br/>&#xA0;&#xA0;belongs_to :user <br/>&#xA0;&#xA0;belongs_to :report <br/> end <br/>

Looks just like the example I linked to you say & you’re right! But, that’s not all. My customer now wants to categorize the reports. So we make a simple lookup table called `report_types`. It has `id`, `report_type_name`, `created_on`, & `updated_on` fields. We then add the field `report_type_id` to the `reports` table. Next we generate a model for `report_types` & then add

has_many :reports

to the ReportType class (report_type.rb) and we add

belongs_to :report_type

to the Report class we previously created.

If you have more than one lookup table just repeat the above procedure for adding more lookup tables.

So we now have everything setup properly, but how do we output the value from the lookup table? Quite easily in fact. We’ll look at two examples. First we are going to display all report contributions for all users. So we generate a new controller called `reports`. Then we create an index method.

class ReportController &lt; ApplicationController <br/> &#xA0;&#xA0;def index<br/> &#xA0;&#xA0;&#xA0;&#xA0; @reports = Report.find_all<br/> &#xA0;&#xA0;end<br/> end<br/>

we then add another method `by_user` to the `Report` controller which will display reports contributed to by a single user.

def index<br/> &#xA0;&#xA0; @writer = User.find(@params["id"]) <br/>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;# assume the id is passed as a parameter<br/> &#xA0;&#xA0; @reports = @writer.reports.find_all<br/> end<br/>

Now that we gotten our data out of the database, how do i display the actual report type and not the id you ask? It’s as simple as adding the line

  1. inside a loop such as:
  2. @reports.each do |userreport|
    <%= userreport.report_type.report_type_name %>

  3. end of loop

anywhere in your rhtml file. The important thing is to remember to use the association that you put in the belongs_to statement and not the class name.

And there you have it. Lookup table data while using hm:t. Have fun coding!