rails: Model.unscoped block not removing default scope for joins
Summary:
Suppose Model has a default scope.
Model.unscoped { Foo.joins(:model) }
acts as if Model.unscoped { ... }
were not specified. That is, it constructs Foo.joins(:model)
as if it weren’t inside Model.unscoped
, so that Model.default_scope
is mistakenly continuing to apply to the joins
, even though unscoped
should have removed it.
Consider a User
with a default_scope
that specifies status = 1
:
>> User.all.to_sql
#=> "SELECT `users`.* FROM `users` WHERE `users`.`status` = 1"
If an Item
belongs to User
, trying to join the two with Item.joins(:user)
seems to always respect User.default_scope
, no matter how you try to remove the default scope. So the joins
SQL
>> Item.joins(:user).to_sql # includes User.default_scope, CORRECTLY.
#=> "SELECT `items`.* FROM `items` INNER JOIN `users` ON `users`.`id` = `items`.`user_id` AND `users`.`status` = 1
correctly includes
`users`.`status` = 1
as it should, but if you try to remove it with User.unscoped { ... }
, the status = 1
default scope on User
is still applying, which I believe is a bug:
User.unscoped do
Item.joins(:user) # User is incorrectly still scoped to status == 1 here
end
As a total aside, it would be nice to have something like joins_unscoped(:user)
as an alternative.
About this issue
- Original URL
- State: closed
- Created 10 years ago
- Reactions: 7
- Comments: 44 (24 by maintainers)
Commits related to this issue
- Merge pull request #18109 from k0kubun/unscoped-joins Allow `joins` to be unscoped Fixes #13775 — committed to rails/rails by sgrif 8 years ago
@miguelgraz I’ve been facing the same issue, and if you don’t write a raw join (which in some cases can be pretty painful), you could go for this which works.
Based on the models:
In order to be able to do a
joins
onItem
toUser
without being impacted with the default scope, you could do this:Then in order to skip the default scope when doing the join you just do:
This is probably the worst bug in active record. It basically makes default scope completely unusable if you have any sort of complexity in your business logic. I wish default_scope came with a warning about this issue.
The only workaround I’ve found is to do the join in raw sql.
@aprescott @miguelgraz @rafaelfranca I just created the following https://github.com/rails/rails/pull/16531 that will allow one to say…
Although, ActiveRecord::Base.unscoped { query_that_should_be_unscoped } still doesn’t work (most likely due to the same reasons I mentioned in the PRs commit), I hope this helps until then.
@miguelgraz I am also facing the same problem while doing join default scope also gets called.
Do you have any solution for this?
I’ve been working on a new feature to potentially resolve this problem, as well as the other issues highlighted in #10643.
I accomplished it by introducing a new “inline scope,” and also standardizing the order in which scopes are prioritized: 1) default, 2) association scope, 3) inline scope.
Say you have a
has_many :comments
association that applies a scope that hides soft-deletes via a :deleted_at attribute. You can undo the association scope with an inline scope like this:Nested joins are possible within inline scopes. For the sake of clarity, these two lines are equivalent:
Code here, still needs to be prepped for a PR but wanted to share first:
https://github.com/sidot3291/rails/commit/1735537aec94b149544bd3d2419d0cd0e395ab9d
If anyone wonder about this issue me and @claptimes5 are already working on it, we managed to write a test and, if we’re doing everything right, the bug still seems to exist on master.
This is our base spec to work on, if you guys want to take a look and/or point something wrong: https://github.com/miguelgraz/rails/commit/8cb2132f7496f10af3f637d7f8342572e299569c