How to use unscoped on associated relations in Rails3?

Ruby on-RailsRuby on-Rails-3ScopeArel

Ruby on-Rails Problem Overview


I have a default scope on products due to information security constraints.

class Product < ActiveRecord::Base
  has_many :photos
  
  default_scope where('visible = 1')
end

In my associated Photo model, however, I also have to find products that should not be visible.

class Photo < ActiveRecord::Base
  belongs_to :product
end

my_photo.product

In other cases, I can use unscoped in order to bypass the default_scope, e.g. in Product.unscoped.find_by_title('abc'). However:

How to remove the scope when using associations of a record?

my_photo.unscoped.product does not make sense as my_photo does not have a method called unscoped. Neither does my_photo.product.unscoped make sense as my_photo.product may already be nil.

Ruby on-Rails Solutions


Solution 1 - Ruby on-Rails

Oh. I fooled myself. Thought the following would not work... but it does:

Product.unscoped do
  my_photo.product
end

Notice that you have to call unscoped on the model with the default_scope that should be bypassed.

Also, inheritance has to be respected. If you have class InsuranceProduct < Productand class FinancialProduct < Product and a default_scope in Product, all of the following two combinations will work:

InsuranceProduct.unscoped do
  my_record.insurance_products
end

FinancialProduct.unscoped do
  my_record.financial_products
end

Product.unscoped do
  my_record.products
end

However, the following will not work although the scope is defined in Product:

Product.unscoped do
  my_record.financial_products
end

I guess that's another quirk of STI in Ruby / Rails.

Solution 2 - Ruby on-Rails

Another option is to override the getter method and unscope super:

class Photo < ActiveRecord::Base
  belongs_to :product

  def product
    Product.unscoped{ super }
  end
end

I ran into the same situation where I had one associated model that needed to be unscoped, but in almost every other case it needed the default scope. This should save you the extra calls to unscoped if you are using the assocation getter in more than one place.

Solution 3 - Ruby on-Rails

I'm probably a bit late to the party, but some time ago I found myself in the same situation and I wrote a gem to do this easily: unscoped_associations.

Usage:

belongs_to :user, unscoped: true

Support for:

  • belongs_to
  • has_one
  • has_many

Polymorphic associations are also supported.

Solution 4 - Ruby on-Rails

If you need for a specific association to always be unscoped, you can unscope it when defining the association:

belongs_to :product, -> { unscope(where: :visible) }

For some reason, the specific where key wasn't loading correctly for me, so I just unscoped the entire where, which is another option that happens to work out in my case:

belongs_to :product, -> { unscope(:where) }

The other answers are worth looking at too, but this is another option for Rails 4.1+.

Solution 5 - Ruby on-Rails

In Rails 4 you can use the association with an explicit unscope of the undesirable filter i.e. my_photo.product.unscope(where: :visible)

Solution 6 - Ruby on-Rails

It's not on the main topic but on your problem with ActiveRecord#becomes: We (hopefully) fixed it with an initializer

class ActiveRecord::Base

def becomes_with_association_cache(klass) became = becomes_without_association_cache(klass) became.instance_variable_set("@association_cache", @association_cache) became end alias_method_chain :becomes, :association_cache

end

https://gist.github.com/2478161

Solution 7 - Ruby on-Rails

New answer

This question should help you figure out how to bypass the default where clause for your association.

It's worth repeating though that if you're regularly having to avoid a scope then it probably should be a default. Create a visible non-default scope and use that explicitly in your associations.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestioncrispyView Question on Stackoverflow
Solution 1 - Ruby on-RailscrispyView Answer on Stackoverflow
Solution 2 - Ruby on-RailsjohnmcalileyView Answer on Stackoverflow
Solution 3 - Ruby on-RailsmarketsView Answer on Stackoverflow
Solution 4 - Ruby on-RailsChris PetersView Answer on Stackoverflow
Solution 5 - Ruby on-RailsKamranView Answer on Stackoverflow
Solution 6 - Ruby on-Railsno-dashesView Answer on Stackoverflow
Solution 7 - Ruby on-RailsnoodlView Answer on Stackoverflow