<?xml version="1.0" encoding="UTF-8"?>
<feed xml:lang="en-US" xmlns="http://www.w3.org/2005/Atom">
  <title>PaulBarry.com - Navigating Object Graphs in Rails</title>
  <subtitle type="html">My thoughts, ideas, questions and concerns on technology, sports, music and life</subtitle>
  <id>tag:paulbarry.com,2007:Paulbarry.com</id>
  <generator uri="http://www.paulbarry.com" version="3.0">PaulBarry.com</generator>
  <link href="http://paulbarry.com/xml/atom/article/4800/feed.xml" rel="self" type="application/atom+xml"/>
  <link href="http://paulbarry.com/articles/2007/02/13/navigating-object-graphs-in-rails" rel="alternate" type="text/html"/>

  <updated>2009-01-05T21:48:24-05:00</updated>
  <entry>
    <author>
      <name>Paul Barry</name>
      <email>mail@paulbarry.com</email>
    </author>
    <id>urn:uuid:4cbd021b-60c2-4f20-b9b9-e48adc2bd623</id>

    <published>2007-02-13T21:26:48-05:00</published>
    <updated>2007-02-13T21:26:48-05:00</updated>
    <title type="html">Navigating Object Graphs in Rails</title>
    <link href="http://paulbarry.com/articles/2007/02/13/navigating-object-graphs-in-rails" rel="alternate" type="text/html"/>

    <category term="technology" scheme="http://paulbarry.com/articles/category/technology" label="Technology"/>
        <category term="Rails" scheme="http://paulbarry.com/articles/tag/rails"/>
    <category term="Ruby" scheme="http://paulbarry.com/articles/tag/ruby"/>
        <summary type="html">&lt;p&gt;One of the more powerful and complicated features that good ORM frameworks have is the ability to easily navigate through relationships when constructing queries.  What we&apos;d like out of our ORM framework is to be able to build these complicated queries without having to write any SQL, and as luck would have it, &lt;a href=&quot;http://www.rubyonrails.com&quot;&gt;Ruby on Rails&lt;/a&gt; allows us to do just that.&lt;/p&gt;

&lt;p&gt;We are going to build a simple working example, so if you have Rails 1.2 and MySQL installed, feel free to play along at home.  Our example is a bare minimum simple model representing an online store.  What we want to do is to get all of the users who have ordered a given product.  Seems simple enough, but it will require us to go across a few different models.  So first, let&apos;s go over what the model of our database will be.&lt;/p&gt;

&lt;p&gt;First, we have a simple user table:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;+----------+--------------+------+-----+---------+----------------+
| Field    | Type         | Null | Key | Default | Extra          |
+----------+--------------+------+-----+---------+----------------+
| id       | int(11)      | NO   | PRI | NULL    | auto_increment |
| username | varchar(255) | YES  |     | NULL    |                |
+----------+--------------+------+-----+---------+----------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Just two fields, a primary key and a username.  Obviously a real world database would have more, but they don&apos;t effect what we are doing here.  Next, we have a products table:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;+-------+--------------+------+-----+---------+----------------+
| Field | Type         | Null | Key | Default | Extra          |
+-------+--------------+------+-----+---------+----------------+
| id    | int(11)      | NO   | PRI | NULL    | auto_increment |
| name  | varchar(255) | YES  |     | NULL    |                |
| price | decimal(8,2) | YES  |     | NULL    |                |
+-------+--------------+------+-----+---------+----------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The fields should be pretty self-explanatory.  We have an orders table:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;+---------+---------+------+-----+---------+----------------+
| Field   | Type    | Null | Key | Default | Extra          |
+---------+---------+------+-----+---------+----------------+
| id      | int(11) | NO   | PRI | NULL    | auto_increment |
| user_id | int(11) | YES  |     | NULL    |                |
+---------+---------+------+-----+---------+----------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In this case user_id is a foreign key to the user who placed the order.  And lastly, we have a table to hold the line items for an order:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;+------------+---------+------+-----+---------+----------------+
| Field      | Type    | Null | Key | Default | Extra          |
+------------+---------+------+-----+---------+----------------+
| id         | int(11) | NO   | PRI | NULL    | auto_increment |
| order_id   | int(11) | YES  |     | NULL    |                |
| product_id | int(11) | YES  |     | NULL    |                |
| quantity   | int(11) | YES  |     | NULL    |                |
+------------+---------+------+-----+---------+----------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;A line item has a foreign key to an order and a product.  So, the task at hand is to get all users who have ordered a given product, so we&apos;ll need to join all these tables to get our data.  Now let&apos;s build the rails app and make it happen:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;C:\ruby\projects&amp;gt;rails store
C:\ruby\projects\store&amp;gt;cd store
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now let&apos;s create a database and user for our store:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;C:\ruby\projects\store&amp;gt;mysql -u root -p
Enter password: ********
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 36 to server version: 5.0.27-community-nt

Type &apos;help;&apos; or &apos;\h&apos; for help. Type &apos;\c&apos; to clear the buffer.

mysql&amp;gt; CREATE DATABASE store_development;
Query OK, 1 row affected (0.00 sec)

mysql&amp;gt; CREATE USER store@localhost;
Query OK, 0 rows affected (0.00 sec)

mysql&amp;gt; GRANT ALL PRIVILEGES ON store_development.* TO store@localhost;
Query OK, 0 rows affected (0.00 sec)

mysql&amp;gt; quit
Bye
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now we&apos;ll log in with our new user to make sure everything is working:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;C:\ruby\projects&amp;gt;mysql -u store store_development
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 12 to server version: 5.0.27-community-nt

Type &apos;help;&apos; or &apos;\h&apos; for help. Type &apos;\c&apos; to clear the buffer.

mysql&amp;gt; quit
Bye
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Ok, all is well, let&apos;s edit the rails config to use this database:    &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;C:\ruby\projects\store&amp;gt;notepad config\database.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;NOTEPAD?  NOTEPAD!!?  I know, I know, what can I say.  I&apos;m on Windows and the arrogant makers of &lt;a href=&quot;http://macromates.com&quot;&gt;TextMate&lt;/a&gt; are going to port it to Windows.  But fear not, I am not using the regular notepad, I&apos;m using &lt;a href=&quot;http://notepad-plus.sourceforge.net&quot;&gt;Notepad++&lt;/a&gt;.  Notepad++, ah ha!  It is ++, so it must be better!  Well, it allows you to edit multiple files at once and has syntax highlighting, so until &lt;a href=&quot;http://intype.info&quot;&gt;intype&lt;/a&gt; gets the ability to edit multiple files at once, has a file explorer and can do undo/redo, Notepad++ will have to do.  So make the development settings look like this:    &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;development:
  adapter: mysql
  database: store_development
  username: store
  password:
  host: localhost
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;For safety&apos;s sake, check to make sure the DB connection is working.    &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;C:\ruby\projects\store&amp;gt;rake db:migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This command should return nothing (except possibly a warning about &apos;require_gem&apos;, buy you can safely ignore that).  As they say, no news is good news.  If you do get some kind of error, do not pass go, do not collect $200, figure out what is wrong.&lt;/p&gt;

&lt;p&gt;Ok time, to create our objects&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;C:\ruby\projects\store&amp;gt;ruby script/generate model user
C:\ruby\projects\store&amp;gt;notepad db\migrate\001_create_users.rb
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;001_create_users.rb&lt;/code&gt; will contain the standard migration skeleton, which looks like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class CreateUsers &amp;lt; ActiveRecord::Migration
  def self.up
    create_table :users do |t|
    end
  end

  def self.down
    drop_table :users
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;So we&apos;ll just add one line between the create_table and the end, that contains this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;t.column :username, :string
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Each subsequent migration will contain only one table, so I&apos;ll just give what goes inside the create table.  We are going to create the simplest possible model, with as few proprties as possible.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;C:\ruby\projects\store&amp;gt;ruby script/generate model product
C:\ruby\projects\store&amp;gt;notepad db\migrate\002_create_products.rb    

t.column :name, :string
t.column :price, :decimal, :precision =&amp;gt; 8, :scale =&amp;gt; 2

C:\ruby\projects\store&amp;gt;ruby script/generate model order
C:\ruby\projects\store&amp;gt;notepad db\migrate\003_create_orders.rb

t.column :user_id, :integer

C:\ruby\projects\store&amp;gt;ruby script/generate model line_item
C:\ruby\projects\store&amp;gt;notepad db\migrate\004_create_line_items.rb    

t.column :order_id, :integer
t.column :product_id, :integer
t.column :quantity, :integer
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now we&apos;ll run the rake task to create the database structure.  &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;C:\ruby\projects\store&amp;gt;rake db:migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now that we have the database structure, we need to edit the code for the models to define the relationships.    &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;C:\ruby\projects\store&amp;gt;notepad app\models\order.rb

class Order &amp;lt; ActiveRecord::Base
  belongs_to :user
end

C:\ruby\projects\store&amp;gt;notepad app\models\product.rb

class Product &amp;lt; ActiveRecord::Base
  has_many :line_items
end

C:\ruby\projects\store&amp;gt;notepad app\models\line_item.rb

class LineItem &amp;lt; ActiveRecord::Base
  belongs_to :order
  belongs_to :product
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now that we have the model defined, let&apos;s create some test data:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;C:\ruby\projects\store&amp;gt;ruby script/generate migration create_test_data

class CreateTestData &amp;lt; ActiveRecord::Migration
  def self.up
    #Create a product
    foo = Product.new(:name =&amp;gt; &apos;foo&apos;, :price =&amp;gt; 9.99)
    foo.save

    #Create another product
    bar = Product.new(:name =&amp;gt; &apos;bar&apos;, :price =&amp;gt; 14.99)
    bar.save

    #Create our first user, me
    pbarry = User.new(:username =&amp;gt; &apos;pbarry&apos;)
    pbarry.save

    #Create some other guy
    jsmith = User.new(:username =&amp;gt; &apos;jsmith&apos;)
    jsmith.save

    #Create our first order
    #notice all the line items and the order get saved with just one order.save
    order = Order.new(:user =&amp;gt; pbarry)
    order.line_items &amp;lt;&amp;lt; LineItem.new(:product =&amp;gt; foo, :quantity =&amp;gt; 2, :order =&amp;gt; order)
    order.line_items &amp;lt;&amp;lt; LineItem.new(:product =&amp;gt; bar, :quantity =&amp;gt; 1, :order =&amp;gt; order)
    order.save

    #What can I say, I just can&apos;t get enough foo.
    order = Order.new(:user =&amp;gt; pbarry)
    order.line_items &amp;lt;&amp;lt; LineItem.new(:product =&amp;gt; foo, :quantity =&amp;gt; 1, :order =&amp;gt; order)
    order.save

    #Random guy order&apos;s something too
    order = Order.new(:user =&amp;gt; jsmith)
    order.line_items &amp;lt;&amp;lt; LineItem.new(:product =&amp;gt; bar, :quantity =&amp;gt; 1, :order =&amp;gt; order)
    order.save
  end

  def self.down
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;So now let&apos;s run this migration:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;C:\ruby\projects\store&amp;gt;rake db:migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now our database is full of data.  Go ahead and check if you don&apos;t believe me.  And we didn&apos;t write any SQL.  Nice.  &lt;/p&gt;

&lt;p&gt;Now here&apos;s where the fun starts.  Let&apos;s get those users who have ordered &apos;foo&apos;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;C:\ruby\projects\store&amp;gt;ruby script/console
Loading development environment.
&amp;gt;&amp;gt; User.find(:all, :include =&amp;gt; {:orders =&amp;gt; {:line_items =&amp;gt; :product}}, \
?&amp;gt; :conditions =&amp;gt; [&quot;products.name = ?&quot;, &apos;foo&apos;])[0].username
=&amp;gt; &quot;pbarry&quot;
&amp;gt;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Whammy!  That was easy.  We didn&apos;t write any SQL to make any of those joins happen.  The nested hashes in &lt;code&gt;:include&lt;/code&gt; did all the work.  We also didn&apos;t have to do anything funky with the results, we got back an array of users, just like we asked for.  And we did it all in one line of code (ok two lines, broken only for the sake of readability).  Not bad.&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;One of the more powerful and complicated features that good ORM frameworks have is the ability to easily navigate through relationships when constructing queries.  What we&apos;d like out of our ORM framework is to be able to build these complicated queries without having to write any SQL, and as luck would have it, &lt;a href=&quot;http://www.rubyonrails.com&quot;&gt;Ruby on Rails&lt;/a&gt; allows us to do just that.&lt;/p&gt;

&lt;p&gt;We are going to build a simple working example, so if you have Rails 1.2 and MySQL installed, feel free to play along at home.  Our example is a bare minimum simple model representing an online store.  What we want to do is to get all of the users who have ordered a given product.  Seems simple enough, but it will require us to go across a few different models.  So first, let&apos;s go over what the model of our database will be.&lt;/p&gt;

&lt;p&gt;First, we have a simple user table:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;+----------+--------------+------+-----+---------+----------------+
| Field    | Type         | Null | Key | Default | Extra          |
+----------+--------------+------+-----+---------+----------------+
| id       | int(11)      | NO   | PRI | NULL    | auto_increment |
| username | varchar(255) | YES  |     | NULL    |                |
+----------+--------------+------+-----+---------+----------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Just two fields, a primary key and a username.  Obviously a real world database would have more, but they don&apos;t effect what we are doing here.  Next, we have a products table:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;+-------+--------------+------+-----+---------+----------------+
| Field | Type         | Null | Key | Default | Extra          |
+-------+--------------+------+-----+---------+----------------+
| id    | int(11)      | NO   | PRI | NULL    | auto_increment |
| name  | varchar(255) | YES  |     | NULL    |                |
| price | decimal(8,2) | YES  |     | NULL    |                |
+-------+--------------+------+-----+---------+----------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The fields should be pretty self-explanatory.  We have an orders table:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;+---------+---------+------+-----+---------+----------------+
| Field   | Type    | Null | Key | Default | Extra          |
+---------+---------+------+-----+---------+----------------+
| id      | int(11) | NO   | PRI | NULL    | auto_increment |
| user_id | int(11) | YES  |     | NULL    |                |
+---------+---------+------+-----+---------+----------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In this case user_id is a foreign key to the user who placed the order.  And lastly, we have a table to hold the line items for an order:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;+------------+---------+------+-----+---------+----------------+
| Field      | Type    | Null | Key | Default | Extra          |
+------------+---------+------+-----+---------+----------------+
| id         | int(11) | NO   | PRI | NULL    | auto_increment |
| order_id   | int(11) | YES  |     | NULL    |                |
| product_id | int(11) | YES  |     | NULL    |                |
| quantity   | int(11) | YES  |     | NULL    |                |
+------------+---------+------+-----+---------+----------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;A line item has a foreign key to an order and a product.  So, the task at hand is to get all users who have ordered a given product, so we&apos;ll need to join all these tables to get our data.  Now let&apos;s build the rails app and make it happen:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;C:\ruby\projects&amp;gt;rails store
C:\ruby\projects\store&amp;gt;cd store
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now let&apos;s create a database and user for our store:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;C:\ruby\projects\store&amp;gt;mysql -u root -p
Enter password: ********
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 36 to server version: 5.0.27-community-nt

Type &apos;help;&apos; or &apos;\h&apos; for help. Type &apos;\c&apos; to clear the buffer.

mysql&amp;gt; CREATE DATABASE store_development;
Query OK, 1 row affected (0.00 sec)

mysql&amp;gt; CREATE USER store@localhost;
Query OK, 0 rows affected (0.00 sec)

mysql&amp;gt; GRANT ALL PRIVILEGES ON store_development.* TO store@localhost;
Query OK, 0 rows affected (0.00 sec)

mysql&amp;gt; quit
Bye
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now we&apos;ll log in with our new user to make sure everything is working:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;C:\ruby\projects&amp;gt;mysql -u store store_development
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 12 to server version: 5.0.27-community-nt

Type &apos;help;&apos; or &apos;\h&apos; for help. Type &apos;\c&apos; to clear the buffer.

mysql&amp;gt; quit
Bye
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Ok, all is well, let&apos;s edit the rails config to use this database:    &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;C:\ruby\projects\store&amp;gt;notepad config\database.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;NOTEPAD?  NOTEPAD!!?  I know, I know, what can I say.  I&apos;m on Windows and the arrogant makers of &lt;a href=&quot;http://macromates.com&quot;&gt;TextMate&lt;/a&gt; are going to port it to Windows.  But fear not, I am not using the regular notepad, I&apos;m using &lt;a href=&quot;http://notepad-plus.sourceforge.net&quot;&gt;Notepad++&lt;/a&gt;.  Notepad++, ah ha!  It is ++, so it must be better!  Well, it allows you to edit multiple files at once and has syntax highlighting, so until &lt;a href=&quot;http://intype.info&quot;&gt;intype&lt;/a&gt; gets the ability to edit multiple files at once, has a file explorer and can do undo/redo, Notepad++ will have to do.  So make the development settings look like this:    &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;development:
  adapter: mysql
  database: store_development
  username: store
  password:
  host: localhost
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;For safety&apos;s sake, check to make sure the DB connection is working.    &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;C:\ruby\projects\store&amp;gt;rake db:migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This command should return nothing (except possibly a warning about &apos;require_gem&apos;, buy you can safely ignore that).  As they say, no news is good news.  If you do get some kind of error, do not pass go, do not collect $200, figure out what is wrong.&lt;/p&gt;

&lt;p&gt;Ok time, to create our objects&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;C:\ruby\projects\store&amp;gt;ruby script/generate model user
C:\ruby\projects\store&amp;gt;notepad db\migrate\001_create_users.rb
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;001_create_users.rb&lt;/code&gt; will contain the standard migration skeleton, which looks like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class CreateUsers &amp;lt; ActiveRecord::Migration
  def self.up
    create_table :users do |t|
    end
  end

  def self.down
    drop_table :users
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;So we&apos;ll just add one line between the create_table and the end, that contains this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;t.column :username, :string
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Each subsequent migration will contain only one table, so I&apos;ll just give what goes inside the create table.  We are going to create the simplest possible model, with as few proprties as possible.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;C:\ruby\projects\store&amp;gt;ruby script/generate model product
C:\ruby\projects\store&amp;gt;notepad db\migrate\002_create_products.rb    

t.column :name, :string
t.column :price, :decimal, :precision =&amp;gt; 8, :scale =&amp;gt; 2

C:\ruby\projects\store&amp;gt;ruby script/generate model order
C:\ruby\projects\store&amp;gt;notepad db\migrate\003_create_orders.rb

t.column :user_id, :integer

C:\ruby\projects\store&amp;gt;ruby script/generate model line_item
C:\ruby\projects\store&amp;gt;notepad db\migrate\004_create_line_items.rb    

t.column :order_id, :integer
t.column :product_id, :integer
t.column :quantity, :integer
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now we&apos;ll run the rake task to create the database structure.  &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;C:\ruby\projects\store&amp;gt;rake db:migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now that we have the database structure, we need to edit the code for the models to define the relationships.    &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;C:\ruby\projects\store&amp;gt;notepad app\models\order.rb

class Order &amp;lt; ActiveRecord::Base
  belongs_to :user
end

C:\ruby\projects\store&amp;gt;notepad app\models\product.rb

class Product &amp;lt; ActiveRecord::Base
  has_many :line_items
end

C:\ruby\projects\store&amp;gt;notepad app\models\line_item.rb

class LineItem &amp;lt; ActiveRecord::Base
  belongs_to :order
  belongs_to :product
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now that we have the model defined, let&apos;s create some test data:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;C:\ruby\projects\store&amp;gt;ruby script/generate migration create_test_data

class CreateTestData &amp;lt; ActiveRecord::Migration
  def self.up
    #Create a product
    foo = Product.new(:name =&amp;gt; &apos;foo&apos;, :price =&amp;gt; 9.99)
    foo.save

    #Create another product
    bar = Product.new(:name =&amp;gt; &apos;bar&apos;, :price =&amp;gt; 14.99)
    bar.save

    #Create our first user, me
    pbarry = User.new(:username =&amp;gt; &apos;pbarry&apos;)
    pbarry.save

    #Create some other guy
    jsmith = User.new(:username =&amp;gt; &apos;jsmith&apos;)
    jsmith.save

    #Create our first order
    #notice all the line items and the order get saved with just one order.save
    order = Order.new(:user =&amp;gt; pbarry)
    order.line_items &amp;lt;&amp;lt; LineItem.new(:product =&amp;gt; foo, :quantity =&amp;gt; 2, :order =&amp;gt; order)
    order.line_items &amp;lt;&amp;lt; LineItem.new(:product =&amp;gt; bar, :quantity =&amp;gt; 1, :order =&amp;gt; order)
    order.save

    #What can I say, I just can&apos;t get enough foo.
    order = Order.new(:user =&amp;gt; pbarry)
    order.line_items &amp;lt;&amp;lt; LineItem.new(:product =&amp;gt; foo, :quantity =&amp;gt; 1, :order =&amp;gt; order)
    order.save

    #Random guy order&apos;s something too
    order = Order.new(:user =&amp;gt; jsmith)
    order.line_items &amp;lt;&amp;lt; LineItem.new(:product =&amp;gt; bar, :quantity =&amp;gt; 1, :order =&amp;gt; order)
    order.save
  end

  def self.down
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;So now let&apos;s run this migration:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;C:\ruby\projects\store&amp;gt;rake db:migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now our database is full of data.  Go ahead and check if you don&apos;t believe me.  And we didn&apos;t write any SQL.  Nice.  &lt;/p&gt;

&lt;p&gt;Now here&apos;s where the fun starts.  Let&apos;s get those users who have ordered &apos;foo&apos;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;C:\ruby\projects\store&amp;gt;ruby script/console
Loading development environment.
&amp;gt;&amp;gt; User.find(:all, :include =&amp;gt; {:orders =&amp;gt; {:line_items =&amp;gt; :product}}, \
?&amp;gt; :conditions =&amp;gt; [&quot;products.name = ?&quot;, &apos;foo&apos;])[0].username
=&amp;gt; &quot;pbarry&quot;
&amp;gt;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Whammy!  That was easy.  We didn&apos;t write any SQL to make any of those joins happen.  The nested hashes in &lt;code&gt;:include&lt;/code&gt; did all the work.  We also didn&apos;t have to do anything funky with the results, we got back an array of users, just like we asked for.  And we did it all in one line of code (ok two lines, broken only for the sake of readability).  Not bad.&lt;/p&gt;
</content>
  </entry>
  </feed>