How to (not) use unscoped in Rails
Tags: rails, ruby
Published: 2019-07-17 16:25:04

---
title: How to (not) use unscoped in Rails
published: true
tags: rails, ruby
canonical_url: https://prathamesh.tech/2019/07/15/how-to-not-use-unscope-in-rails/
---

Active Record provides `unscoped` to remove all the scopes added to a model previously.

<!--kg-card-begin: code-->
```
class Article
  default_scope { where(published: true) }
end

Article.all # SELECT * FROM articles WHERE published = true

Article.unscoped # SELECT * FROM articles
```
<!--kg-card-end: code-->

Let's say I want to fetch all articles of an author, published or otherwise.

<!--kg-card-begin: code-->
```
author = Author.find_by(email: "prathamesh@example.com")
author.articles.unscoped
```
<!--kg-card-end: code-->

I am expecting all articles to be returned for given author. But there is a surprise, instead of getting articles of a specific author, I get back all articles in the database!

<!--kg-card-begin: code-->
```
author.articles.unscoped 
# SELECT "articles".* FROM "articles"
```
<!--kg-card-end: code-->

How did this happen?

When `unscoped` is called on a model, it calls [unscoped](https://github.com/rails/rails/blob/b9ca94caea2ca6a6cc09abaffaad67b447134079/activerecord/lib/active_record/scoping/default.rb#L33) and returns a scope of that model without any previous scopes.

But we called `unscoped` on an association. Let's see where the `unscoped` method is defined on an association.

<!--kg-card-begin: code-->
```
[5] pry(main)> author.articles.method(:unscoped)
=> #<Method: Article::ActiveRecord_Associations_CollectionProxy#unscoped>
[6] pry(main)> author.articles.method(:unscoped).source_location
=> ["/Users/prathamesh/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/activerecord-5.2.3/lib/active_record/relation/delegation.rb",
 96]
```
<!--kg-card-end: code-->

Turns out, we don't have a `unscoped` method defined on the association object. Instead it just delegates to the `unscoped` on the model of the association through various intermediate objects.

The full stack trace of these intermediate calls is as follows:

<!--kg-card-begin: code-->
```
"/Users/prathamesh/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/activerecord-5.2.3/lib/active_record/scoping/default.rb:34:in `unscoped'",
 "/Users/prathamesh/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/activerecord-5.2.3/lib/active_record/associations/association_scope.rb:24:in `scope'",
 "/Users/prathamesh/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/activerecord-5.2.3/lib/active_record/associations/association_scope.rb:7:in `scope'",
 "/Users/prathamesh/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/activerecord-5.2.3/lib/active_record/associations/association.rb:90:in `association_scope'",
 "/Users/prathamesh/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/activerecord-5.2.3/lib/active_record/associations/association.rb:79:in `scope'",
 "/Users/prathamesh/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/activerecord-5.2.3/lib/active_record/associations/collection_association.rb:287:in `scope'",
 "/Users/prathamesh/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/activerecord-5.2.3/lib/active_record/associations/collection_proxy.rb:932:in `scope'",
 "/Users/prathamesh/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/activerecord-5.2.3/lib/active_record/associations/collection_proxy.rb:1104:in `scoping'",
 "/Users/prathamesh/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/activerecord-5.2.3/lib/active_record/relation/delegation.rb:114:in `method_missing'",
 
 "/Users/prathamesh/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/activerecord-5.2.3/lib/active_record/scoping/default.rb:34:in `unscoped'",
 "/Users/prathamesh/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/activerecord-5.2.3/lib/active_record/relation/delegation.rb:114:in `public_send'",
 "/Users/prathamesh/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/activerecord-5.2.3/lib/active_record/relation/delegation.rb:114:in `block in method_missing'",
 "/Users/prathamesh/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/activerecord-5.2.3/lib/active_record/relation.rb:281:in `scoping'",
 "/Users/prathamesh/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/activerecord-5.2.3/lib/active_record/associations/collection_proxy.rb:1104:in `scoping'",
 "/Users/prathamesh/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/activerecord-5.2.3/lib/active_record/relation/delegation.rb:114:in `method_missing'",
```
<!--kg-card-end: code-->

When we are calling `unscoped` on an association, it is actually getting translated to calling `unscoped` on the model itself. So `author.articles.unscoped` gets translated to `Article.unscoped`. That's why we get all the articles without the author constraint back.

This behavior stumped me as I was expecting it to "just work" on associations as well.

If we want to `unscoped` articles of an author, we need to fetch them via articles, not via author.

<!--kg-card-begin: code-->
```
Article.unscoped.where(author: author)
# SELECT "articles".* FROM "articles" WHERE "articles"."author_id" = ?
```
<!--kg-card-end: code-->

So next time you are using `unscoped` in your code, make sure you are not calling accidentally on an association.

**Pro Tip**

Ruby has a secret weapon to debug any program. It is a [method](https://ruby-doc.org/core-2.5.3/Method.html) named `method`. You can call it on any object and pass the method name to get the method object. Once the method object is caught, we can ask its [source location](https://ruby-doc.org/core-2.5.3/Method.html#method-i-source_location). I used it to debug the code in this blog post.