Question

I have a tree-like model where in all situations but one, I want to scope the results to only return the roots.

class Licence < ActiveRecord::Base
  default_scope :conditions => { :parent_licence_id, nil }

  belongs_to :parent_licence, :class_name => 'Licence'
  has_many :nested_licences, :class_name => 'Licence',
           :foreign_key => 'parent_licence_id', :dependent => :destroy
end

class User < ActiveRecord::Base
  has_many :licences
end

Using default_scope seemed like an awesome idea because the various models which have associations to Licence (there are about 4 of them) and also any code using find(), would not need to do anything special. The reason it isn't an awesome idea is that the default scope also applies to the has_many, which results in never finding the children. But the has_many is the only place which needs to bust out of the scope, so as far as "default" behaviour goes, I think this default_scope is quite reasonable.

So is there some good way to get around this specific problem?

Here is one which I am not too fond of because it uses SQL for a nearly trivial query:

has_many :nested_licences, :class_name => 'Licence', :dependent => :destroy,
  :finder_sql => 'SELECT l.* FROM licences l WHERE l.parent_licence_id = #{id}',
  :counter_sql => 'SELECT COUNT(l.*) FROM licences l WHERE l.parent_licence_id = #{id}'

Alternatively is there some way to apply a named scope to an association from the model? e.g. something along the lines of this nonsense-code:

class Licence < ActiveRecord::Base
  named_scope :roots, :conditions => { :parent_licence_id, nil }

  belongs_to :parent_licence, :class_name => 'Licence'
  has_many :nested_licences, :class_name => 'Licence',
           :foreign_key => 'parent_licence_id', :dependent => :destroy
end

class User < ActiveRecord::Base
  has_many :licences, :scope => :roots   # a :scope option doesn't really exist
end

I know I can also do this:

class Licence < ActiveRecord::Base
  named_scope :roots, :conditions => { :parent_licence_id, nil }

  belongs_to :parent_licence, :class_name => 'Licence'
  has_many :nested_licences, :class_name => 'Licence',
           :foreign_key => 'parent_licence_id', :dependent => :destroy
end

class User < ActiveRecord::Base
  has_many :licences, :conditions => { :parent_licence_id, nil }
end

But that really isn't very DRY. Actually having to do every single query through Licence.roots.find() instead of Licence.find() isn't very DRY either, to be honest. It's just asking for a bug to occur where the scope isn't used.

Was it helpful?

Solution

Try using Licence.unscoped.find()

btw - The documentation for ActiveRecord::Base.unscoped says that chaining unscoped with a named scope method has no effect.
It is recommended to use the block form of unscoped because chaining unscoped with a named scope does not work. If "sent" (below) is a named_scope the following two statements are the same.

Message.unscoped.sent
Message.sent  

fyi rails 2 also has with_exclusive_scope which can be helpful.

OTHER TIPS

Can't you make use of the :conditions option on the association? Something like this:

has_many :nested_licences, :class_name => 'Licence',
         :dependent => :destroy, :conditions => "parent_licence_id = #{id}" 
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top