mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Allow reload
to be default_scoped
`reload` is not `default_scoped` by default because you could be creating a record that does not match your default scope and therefore `reload` wouldn't find the record. However, in the case of sharding an application you may want `reload` to support `default_scope` because you'll always have the correct scope set. To accomplish this I added a new method that checks if there are any default scopes defined that are set to run for `all_queries`. If there are then don't `unscope` the find methods. If there is a case where you do want the default scope to apply to all queries but be able to turn that off, I've added a `unscoped` option to `reload`. This would use the original behavior regardless of whether the `default_scope` was used for all queries. Additionally, this exposes a new public method `default_scopes?` that returns true if there are default scopes. If `all_queries` is passed to `default_scopes?` the method will only return true if there is a default scope for the model that has `all_queries` set to true. If there are no default scopes with `all_queries` then it will return false.
This commit is contained in:
parent
3e5d504f78
commit
a3343d2456
3 changed files with 49 additions and 8 deletions
|
@ -378,7 +378,7 @@ module ActiveRecord
|
|||
def _update_record(values, constraints) # :nodoc:
|
||||
constraints = _substitute_values(constraints).map { |attr, bind| attr.eq(bind) }
|
||||
|
||||
if default_scopes.any? && !default_scoped(all_queries: true).where_clause.empty?
|
||||
if default_scopes?(all_queries: true)
|
||||
constraints << default_scoped(all_queries: true).where_clause.ast
|
||||
end
|
||||
|
||||
|
@ -392,7 +392,7 @@ module ActiveRecord
|
|||
def _delete_record(constraints) # :nodoc:
|
||||
constraints = _substitute_values(constraints).map { |attr, bind| attr.eq(bind) }
|
||||
|
||||
if default_scopes.any? && !default_scoped(all_queries: true).where_clause.empty?
|
||||
if default_scopes?(all_queries: true)
|
||||
constraints << default_scoped(all_queries: true).where_clause.ast
|
||||
end
|
||||
|
||||
|
@ -808,12 +808,11 @@ module ActiveRecord
|
|||
def reload(options = nil)
|
||||
self.class.connection.clear_query_cache
|
||||
|
||||
fresh_object =
|
||||
if options && options[:lock]
|
||||
self.class.unscoped { self.class.lock(options[:lock]).find(id) }
|
||||
else
|
||||
self.class.unscoped { self.class.find(id) }
|
||||
end
|
||||
fresh_object = if self.class.default_scopes?(all_queries: true) && !(options && options[:unscoped])
|
||||
find_record(options)
|
||||
else
|
||||
self.class.unscoped { find_record(options) }
|
||||
end
|
||||
|
||||
@attributes = fresh_object.instance_variable_get(:@attributes)
|
||||
@new_record = false
|
||||
|
@ -873,6 +872,14 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
private
|
||||
def find_record(options)
|
||||
if options && options[:lock]
|
||||
self.class.lock(options[:lock]).find(id)
|
||||
else
|
||||
self.class.find(id)
|
||||
end
|
||||
end
|
||||
|
||||
# A hook to be overridden by association modules.
|
||||
def destroy_associations
|
||||
end
|
||||
|
|
|
@ -52,6 +52,17 @@ module ActiveRecord
|
|||
self.current_scope = nil
|
||||
end
|
||||
|
||||
# Checks if the model has any default scopes. If all_queries
|
||||
# is set to true, the method will check if there are any
|
||||
# default_scopes for the model where `all_queries` is true.
|
||||
def default_scopes?(all_queries: false)
|
||||
if all_queries
|
||||
self.default_scopes.map(&:all_queries).include?(true)
|
||||
else
|
||||
self.default_scopes.any?
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
# Use this macro in your model to set a default scope for all operations on
|
||||
# the model.
|
||||
|
|
|
@ -158,6 +158,29 @@ class DefaultScopingTest < ActiveRecord::TestCase
|
|||
assert_match(/mentor_id/, destroy_sql)
|
||||
end
|
||||
|
||||
def test_default_scope_doesnt_run_on_reload
|
||||
Mentor.create!
|
||||
dev = DeveloperwithDefaultMentorScopeNot.create!(name: "Eileen")
|
||||
reload_sql = capture_sql { dev.reload }.first
|
||||
|
||||
assert_no_match(/mentor_id/, reload_sql)
|
||||
end
|
||||
|
||||
def test_default_scope_with_all_queries_runs_on_reload
|
||||
Mentor.create!
|
||||
dev = DeveloperWithDefaultMentorScopeAllQueries.create!(name: "Eileen")
|
||||
reload_sql = capture_sql { dev.reload }.first
|
||||
|
||||
assert_match(/mentor_id/, reload_sql)
|
||||
end
|
||||
|
||||
def test_default_scope_with_all_queries_doesnt_run_on_destroy_when_unscoped
|
||||
dev = DeveloperWithDefaultMentorScopeAllQueries.create!(name: "Eileen", mentor_id: 2)
|
||||
reload_sql = capture_sql { dev.reload({ unscoped: true }) }.first
|
||||
|
||||
assert_no_match(/mentor_id/, reload_sql)
|
||||
end
|
||||
|
||||
def test_scope_overwrites_default
|
||||
expected = Developer.all.merge!(order: "salary DESC, name DESC").to_a.collect(&:name)
|
||||
received = DeveloperOrderedBySalary.by_name.to_a.collect(&:name)
|
||||
|
|
Loading…
Reference in a new issue