Search code examples
ruby-on-railsactiverecordruby-on-rails-5has-many-throughhas-many

Rails Scoped has many through has many with scope


I have a model with a scoped has many. I would like to chain a has many through onto that:


 class X < ApplicationRecord
  has_many :ys, {foreign_key: :y_id} do
    def for_z(z)
      where("boolean_flag = #{z.boolean_flag}")  
    end

  has_many :bs, through: :ys # but I want to get only the bs for ys.for_z(z)
  end

A y here belongs_to a b

so in the end I'd like to call :

something.xs.ys.for(z).bs

right now I can still do

something.xs.ys.for(z).map {|y| y.b}

but I'd like to wire up the association correctly


Solution

  • You need to actually define a second association:

    class Project < ApplicationRecord
      has_many :issues
      has_many :open_issues,
        -> { where(status: 'open') },
        class_name: 'Issue'
        
      has_many :assignees, through: :open_issues
    end
    

    A has_many through: association just takes the name of another association that it joins through. You cannot define an association that goes through an association extension which you are incorrectly referring to as a scope.

    A scope is really just a class method which can be called on any relation (since it proxies to the class) while association extensions can only be called on association proxies.

    If you want to actually create a scoped association, you need to pass a callable such as a lambda.

    has_many :open_issues,
      -> { where(status: 'open') },
      class_name: 'Issue'
    

    This really just applies a set of filters directly to the association itself.

    something.xs.ys.for(z).bs
    

    Is not actually compatible with how associations actually work in Rails. Associations are not callable on relations or association proxy objects - only on records themselves.