mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Fix update & destroy queries when default_scope is nillable with all_queries: true
This commit is contained in:
parent
e21c38ed7f
commit
43d83b4423
3 changed files with 61 additions and 6 deletions
|
@ -431,9 +431,8 @@ module ActiveRecord
|
||||||
def _update_record(values, constraints) # :nodoc:
|
def _update_record(values, constraints) # :nodoc:
|
||||||
constraints = constraints.map { |name, value| predicate_builder[name, value] }
|
constraints = constraints.map { |name, value| predicate_builder[name, value] }
|
||||||
|
|
||||||
if default_scopes?(all_queries: true)
|
default_constraint = build_default_constraint
|
||||||
constraints << default_scoped(all_queries: true).where_clause.ast
|
constraints << default_constraint if default_constraint
|
||||||
end
|
|
||||||
|
|
||||||
if current_scope = self.global_current_scope
|
if current_scope = self.global_current_scope
|
||||||
constraints << current_scope.where_clause.ast
|
constraints << current_scope.where_clause.ast
|
||||||
|
@ -449,9 +448,8 @@ module ActiveRecord
|
||||||
def _delete_record(constraints) # :nodoc:
|
def _delete_record(constraints) # :nodoc:
|
||||||
constraints = constraints.map { |name, value| predicate_builder[name, value] }
|
constraints = constraints.map { |name, value| predicate_builder[name, value] }
|
||||||
|
|
||||||
if default_scopes?(all_queries: true)
|
default_constraint = build_default_constraint
|
||||||
constraints << default_scoped(all_queries: true).where_clause.ast
|
constraints << default_constraint if default_constraint
|
||||||
end
|
|
||||||
|
|
||||||
if current_scope = self.global_current_scope
|
if current_scope = self.global_current_scope
|
||||||
constraints << current_scope.where_clause.ast
|
constraints << current_scope.where_clause.ast
|
||||||
|
@ -479,6 +477,16 @@ module ActiveRecord
|
||||||
def discriminate_class_for_record(record)
|
def discriminate_class_for_record(record)
|
||||||
self
|
self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Called by +_update_record+ and +_delete_record+
|
||||||
|
# to build `where` clause from default scopes.
|
||||||
|
# Skips empty scopes.
|
||||||
|
def build_default_constraint
|
||||||
|
return unless default_scopes?(all_queries: true)
|
||||||
|
|
||||||
|
default_where_clause = default_scoped(all_queries: true).where_clause
|
||||||
|
default_where_clause.ast unless default_where_clause.empty?
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns true if this object hasn't been saved yet -- that is, a record
|
# Returns true if this object hasn't been saved yet -- that is, a record
|
||||||
|
|
|
@ -94,6 +94,12 @@ class DefaultScopingTest < ActiveRecord::TestCase
|
||||||
assert_match(/mentor_id/, create_sql)
|
assert_match(/mentor_id/, create_sql)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_nilable_default_scope_with_all_queries_runs_on_create
|
||||||
|
create_sql = capture_sql { DeveloperWithDefaultNilableMentorScopeAllQueries.create!(name: "Nikita") }.first
|
||||||
|
|
||||||
|
assert_no_match(/AND$/, create_sql)
|
||||||
|
end
|
||||||
|
|
||||||
def test_default_scope_runs_on_select
|
def test_default_scope_runs_on_select
|
||||||
Mentor.create!
|
Mentor.create!
|
||||||
DeveloperwithDefaultMentorScopeNot.create!(name: "Eileen")
|
DeveloperwithDefaultMentorScopeNot.create!(name: "Eileen")
|
||||||
|
@ -110,6 +116,13 @@ class DefaultScopingTest < ActiveRecord::TestCase
|
||||||
assert_match(/mentor_id/, select_sql)
|
assert_match(/mentor_id/, select_sql)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_nilable_default_scope_with_all_queries_runs_on_select
|
||||||
|
DeveloperWithDefaultNilableMentorScopeAllQueries.create!(name: "Nikita")
|
||||||
|
select_sql = capture_sql { DeveloperWithDefaultNilableMentorScopeAllQueries.find_by(name: "Nikita") }.first
|
||||||
|
|
||||||
|
assert_no_match(/AND$/, select_sql)
|
||||||
|
end
|
||||||
|
|
||||||
def test_default_scope_doesnt_run_on_update
|
def test_default_scope_doesnt_run_on_update
|
||||||
Mentor.create!
|
Mentor.create!
|
||||||
dev = DeveloperwithDefaultMentorScopeNot.create!(name: "Eileen")
|
dev = DeveloperwithDefaultMentorScopeNot.create!(name: "Eileen")
|
||||||
|
@ -126,6 +139,13 @@ class DefaultScopingTest < ActiveRecord::TestCase
|
||||||
assert_match(/mentor_id/, update_sql)
|
assert_match(/mentor_id/, update_sql)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_nilable_default_scope_with_all_queries_runs_on_update
|
||||||
|
dev = DeveloperWithDefaultNilableMentorScopeAllQueries.create!(name: "Nikita")
|
||||||
|
update_sql = capture_sql { dev.update!(name: "Not Nikita") }.first
|
||||||
|
|
||||||
|
assert_no_match(/AND$/, update_sql)
|
||||||
|
end
|
||||||
|
|
||||||
def test_default_scope_doesnt_run_on_update_columns
|
def test_default_scope_doesnt_run_on_update_columns
|
||||||
Mentor.create!
|
Mentor.create!
|
||||||
dev = DeveloperwithDefaultMentorScopeNot.create!(name: "Eileen")
|
dev = DeveloperwithDefaultMentorScopeNot.create!(name: "Eileen")
|
||||||
|
@ -142,6 +162,13 @@ class DefaultScopingTest < ActiveRecord::TestCase
|
||||||
assert_match(/mentor_id/, update_sql)
|
assert_match(/mentor_id/, update_sql)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_nilable_default_scope_with_all_queries_runs_on_update_columns
|
||||||
|
dev = DeveloperWithDefaultNilableMentorScopeAllQueries.create!(name: "Nikita")
|
||||||
|
update_sql = capture_sql { dev.update_columns(name: "Not Nikita") }.first
|
||||||
|
|
||||||
|
assert_no_match(/AND$/, update_sql)
|
||||||
|
end
|
||||||
|
|
||||||
def test_default_scope_doesnt_run_on_destroy
|
def test_default_scope_doesnt_run_on_destroy
|
||||||
Mentor.create!
|
Mentor.create!
|
||||||
dev = DeveloperwithDefaultMentorScopeNot.create!(name: "Eileen")
|
dev = DeveloperwithDefaultMentorScopeNot.create!(name: "Eileen")
|
||||||
|
@ -158,6 +185,13 @@ class DefaultScopingTest < ActiveRecord::TestCase
|
||||||
assert_match(/mentor_id/, destroy_sql)
|
assert_match(/mentor_id/, destroy_sql)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_nilable_default_scope_with_all_queries_runs_on_destroy
|
||||||
|
dev = DeveloperWithDefaultNilableMentorScopeAllQueries.create!(name: "Nikita")
|
||||||
|
destroy_sql = capture_sql { dev.destroy }.first
|
||||||
|
|
||||||
|
assert_no_match(/AND$/, destroy_sql)
|
||||||
|
end
|
||||||
|
|
||||||
def test_default_scope_doesnt_run_on_reload
|
def test_default_scope_doesnt_run_on_reload
|
||||||
Mentor.create!
|
Mentor.create!
|
||||||
dev = DeveloperwithDefaultMentorScopeNot.create!(name: "Eileen")
|
dev = DeveloperwithDefaultMentorScopeNot.create!(name: "Eileen")
|
||||||
|
@ -174,6 +208,13 @@ class DefaultScopingTest < ActiveRecord::TestCase
|
||||||
assert_match(/mentor_id/, reload_sql)
|
assert_match(/mentor_id/, reload_sql)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_nilable_default_scope_with_all_queries_runs_on_destroy
|
||||||
|
dev = DeveloperWithDefaultNilableMentorScopeAllQueries.create!(name: "Nikita")
|
||||||
|
reload_sql = capture_sql { dev.reload }.first
|
||||||
|
|
||||||
|
assert_no_match(/AND$/, reload_sql)
|
||||||
|
end
|
||||||
|
|
||||||
def test_default_scope_with_all_queries_doesnt_run_on_destroy_when_unscoped
|
def test_default_scope_with_all_queries_doesnt_run_on_destroy_when_unscoped
|
||||||
dev = DeveloperWithDefaultMentorScopeAllQueries.create!(name: "Eileen", mentor_id: 2)
|
dev = DeveloperWithDefaultMentorScopeAllQueries.create!(name: "Eileen", mentor_id: 2)
|
||||||
reload_sql = capture_sql { dev.reload({ unscoped: true }) }.first
|
reload_sql = capture_sql { dev.reload({ unscoped: true }) }.first
|
||||||
|
|
|
@ -162,6 +162,12 @@ class DeveloperWithDefaultMentorScopeAllQueries < ActiveRecord::Base
|
||||||
default_scope -> { where(mentor_id: 1) }, all_queries: true
|
default_scope -> { where(mentor_id: 1) }, all_queries: true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class DeveloperWithDefaultNilableMentorScopeAllQueries < ActiveRecord::Base
|
||||||
|
self.table_name = "developers"
|
||||||
|
firm_id = nil # Could be something like Current.mentor_id
|
||||||
|
default_scope -> { where(firm_id: firm_id) if firm_id }, all_queries: true
|
||||||
|
end
|
||||||
|
|
||||||
class DeveloperWithIncludes < ActiveRecord::Base
|
class DeveloperWithIncludes < ActiveRecord::Base
|
||||||
self.table_name = "developers"
|
self.table_name = "developers"
|
||||||
has_many :audit_logs, foreign_key: :developer_id
|
has_many :audit_logs, foreign_key: :developer_id
|
||||||
|
|
Loading…
Reference in a new issue