<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DZone Snippets: relationships code</title>
    <link>http://snippets.dzone.com/posts</link>
    <pubDate>Sun, 18 May 2008 00:44:35 GMT</pubDate>
    <description>DZone Snippets: relationships code</description>
    <item>
      <title>The beauty of a nested ':include' option</title>
      <link>http://snippets.dzone.com/posts/show/2089</link>
      <description>So you've got a lot of stuff, spread over a lot of tables, and now you want to scoop it all up and display it all in the same view - oh, and you don't want ActiveRecord to perform a bazillion subsidiary SQL lookups along the way. The answer - a nested :include clause.&lt;br /&gt;&lt;br /&gt;The example:&lt;br /&gt;&lt;br /&gt;A model...&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;class Organizer &lt; ActiveRecord::Base&lt;br /&gt;	has_many :events&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;class Event &lt; ActiveRecord::Base&lt;br /&gt;	belongs_to :organizer&lt;br /&gt;	has_many :bookings&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;class Booking &lt; ActiveRecord::Base&lt;br /&gt;	has_many :sessions&lt;br /&gt;	belongs_to :event&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;class Session &lt; ActiveRecord::Base&lt;br /&gt;	belongs_to :booking&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;... and in my controller i want to...&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;Booking.find_all_by_date(Date.today)&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Now this all works fine... up until a certain line in my '_booking' view partial:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&lt;%= h(booking.event.organizer.name) %&gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;...and a little further along, when i do a:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&lt;%= render :partial =&gt; 'session', :collection =&gt; booking.sessions %&gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Both of which work fine too. Unfortunately, my view has to execute the 'booking' partial a total of three times, and each time it does so, ActiveRecord performs three additional SQL queries - one for the events table lookup, one for the organizers table lookup and one for the sessions table lookup. Along with the original 'bookings' query, that comes to a total of 10 SQL queries to display the details of three bookings. Hardly very efficient.&lt;br /&gt;&lt;br /&gt;:include to the rescue (maybe):&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;Booking.find_all_by_date(Date.today, :include =&gt; [:event, :sessions])&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Sure enough, this pre-loads (or eager loads) the events and sessions table info into the model, thus allowing me to use a line like 'booking.event.title', or 'booking.sessions[0].start_time' without firing off another SQL query. So now my query count drops to just four - one to fetch the combined booking/event/sessions data and the other three to fetch each organizer. All good.&lt;br /&gt;&lt;br /&gt;So, then I tried this:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;Booking.find_all_by_date(Date.today, :include =&gt; [:event, :organizer, :sessions])&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Wrong!!&lt;br /&gt;&lt;br /&gt;I got an error because ':organizer' is not a valid association of the Booking model (ie, there's no 'has_one', 'has_many' or 'belongs_to' referring to the Organizer model from the Booking model)&lt;br /&gt;&lt;br /&gt;I flailed around on the internet for a few hours searching for (and trying out) a number of solutions - mostly revolving around 'find_by_sql'. Though a couple of these solutions successfully fetched the data, they didn't then populate the model objects correctly. Instead, 'find_by_sql' seems to add new accessors to my Booking class - this would allow me to use a line like 'booking.organizer.name', but a call to the real object (booking.event.organizer.name) would still generate a SQL query. Not acceptable.&lt;br /&gt;&lt;br /&gt;The most interesting possibility I found was using a :through clause. This allows you to refer to one association 'through' another like this:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;class Booking &lt; ActiveRecord::Base&lt;br /&gt;	belongs_to :event&lt;br /&gt;	belongs_to :organizer, :through =&gt; :event&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Unfortunately, the :through clause only works when you're passing :through a 'has_many' association. It doesn't work for 'has_one' or 'belongs_to' (didn't try 'has_and_belongs_to').&lt;br /&gt;&lt;br /&gt;So, finally, in a fit of mingled frustration/inspiration (and knowing the nested nature of some of the rails code) i tried this:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;Booking.find_all_by_date(Date.today, :include =&gt; [{:event, :organizer}, :sessions ])&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;My reasoning: well, since :organizer is a valid association of the Event model, I thought that if I packaged those two together in a kind of sub-include, it might actually do a nested call to whichever magic rails method constructs the SQL queries.&lt;br /&gt;&lt;br /&gt;I've since found out that the correct syntax for this clause should be  ':include =&gt; [{:event =&gt; :organizer}, :sessions ]'. Apparantly when you throw an even number of comma-separated items into a hash literal, ruby groups them into :key =&gt; :value pairs for you - what an amazing language it is! :-) So the corrected code snipped should be:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;Booking.find_all_by_date(Date.today, :include =&gt; [{:event =&gt; :organizer}, :sessions ])&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;And Eureka!!&lt;br /&gt;&lt;br /&gt;A single (hefty) SQL query fetched all relevant data, whereupon ActiveRecord dutifully (and apparently recursively) populated all the model objects that my view template needed.&lt;br /&gt;&lt;br /&gt;I'm happy.&lt;br /&gt;&lt;br /&gt;For reference: I'm using Ruby 1.8.4, Rails 1.1 and the SQLite3 database.</description>
      <pubDate>Tue, 23 May 2006 19:20:36 GMT</pubDate>
      <guid>http://snippets.dzone.com/posts/show/2089</guid>
      <author>thalrond (Philip Martin)</author>
    </item>
    <item>
      <title>Doing a has_and_belongs_to_many against a single table</title>
      <link>http://snippets.dzone.com/posts/show/284</link>
      <description>Found via the RoR wiki. Solution courtesy of Jakob S.&lt;br /&gt;&lt;br /&gt;Assuming a schema like such:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;create table node (&lt;br /&gt;  id integer primary key,&lt;br /&gt;  name varchar(32)&lt;br /&gt;);&lt;br /&gt;&lt;br /&gt;create table nodelink (&lt;br /&gt;  source_id integer,&lt;br /&gt;  destination_id integer&lt;br /&gt;);&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;A Rails association like this would supposedly do the job:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;has_and_belongs_to_many :nodes, :join_table =&gt; 'nodelink', :association_foreign_key =&gt; 'destination_id', :foreign_key =&gt; 'source_id'&lt;/code&gt;</description>
      <pubDate>Sat, 14 May 2005 09:44:21 GMT</pubDate>
      <guid>http://snippets.dzone.com/posts/show/284</guid>
      <author>peter (Peter Cooperx)</author>
    </item>
  </channel>
</rss>
