has_one :through

June 20th, 2007

Via: http://idm.s9.xrea.com/ratio/2006/08/04/000496.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class ActiveRecord::Associations::HasOneThroughAssociation < ActiveRecord::Associations::HasOneAssociation
  private
    def construct_sql
      @finder_sql = "#{@reflection.through_reflection.table_name}.#{@reflection.through_reflection.primary_key_name} = #{@owner.quoted_id}"
      @finder_sql << " AND (#{conditions})" if conditions
    end

    def find_target
      @reflection.klass.find(:first,
        :select => @reflection.options[:select] || "#{@reflection.table_name}.*",
        :conditions => @finder_sql,
        :order => @reflection.options[:order],
        :include => @reflection.options[:include],
        :joins => "LEFT OUTER JOIN #{@reflection.through_reflection.table_name} " +
                  " ON #{@reflection.through_reflection.table_name}.#{@reflection.source_reflection.primary_key_name} " +
                "    = #{@reflection.table_name}.#{@reflection.klass.primary_key}"
      )
    end
end

module ActiveRecord::Associations::ClassMethods
  def has_one_with_through(association_id, options = {})
    if options[:through]
      reflection = create_has_one_through_reflection(association_id, options)
      association_accessor_methods(reflection, ActiveRecord::Associations::HasOneThroughAssociation)
    else
      has_one_without_through(association_id, options)
    end
  end

  alias_method :has_one_without_through, :has_one
  alias_method :has_one, :has_one_with_through

  private
    def create_has_one_through_reflection(association_id, options)
      options.assert_valid_keys(
        :class_name, :foreign_key, :remote, :conditions, :order, :include, :dependent, :counter_cache, :extend, :as, :through
      )
      create_reflection(:has_one, association_id, options, self)
    end
end

6 Responses to “has_one :through”

  1. Jonathan Miller Says:

    I don’t get this post…

  2. Ben Says:

    emmm, that’s some problem with link you gave…

  3. Ryan Says:

    Sorry to post this with no explanation – it’s an answer to a specific programming question that I’ve been having: how to set up a have_one :through association with Rails.

    Given models Newsletter and Source, where a newsletter has one source, and a source has many newsletters, I wanted a way to have a join table with extra information (such as created_by to see who created the association). has_one :through is missing from Rails, so the code snippet above mixes it in.

    So now, I can do this:

      class Newsletter < ActiveRecord::Base
        has_one :sourcerers
        has_one :source, :through => :sourcerers
      end
    
      class NewsletterSource < ActiveRecord::Base
        has_many :sourcerers
        has_many :newsletters, :through => :sourcerers
      end
    
      class Sourcerers < ActiveRecord::Base
        belongs_to :source
        belongs_to :newsletter
      end
    
  4. Ustin Metka Says:

    You need to post it to http://dev.rubyonrails.org/ as a path. It will be very usefull for RoR community =)

  5. Renaud Dubois Says:

    Thanks a lot, I was just looking for a such relation ! Can you explain where we have to put this new class ? I tried to create a file named “has_one_through_association.rb” in ”..lib/activerecord/associations” but it’s not recognized

    As Ustin said, you should post it on the ror website, it’s currently a missing feature.

    Renaud

  6. Chris O'Sullivan Says:

    I took the liberty of nicking your code and bunging it into a Rails patch.

    Check it out here: http://dev.rubyonrails.org/ticket/4756

    Thanks! -Chris

Sorry, comments are closed for this article.