ActiveRecord: объединение с незаданной областью не пропускает default_scope - PullRequest
2 голосов
/ 01 июня 2019

У меня возникают некоторые проблемы при попытке пропустить default_scope при выполнении ActiveRecord join.

Несмотря на то, что кодовая база довольно большая, я просто показываю основы, так как думаю, что она довольно хорошо показывает, в чем проблема:

class Client
  belongs_to :company
end

class Company
  default_scope { where(version_id: nil) }
end

Я создаю сложный отчет, поэтому мне нужно объединить несколько таблиц и отфильтровать их. Однако я не могу успешно пропустить область по умолчанию при получении Client s.

Client.joins(:company).to_sql
# SELECT clients.* FROM clients INNER JOIN companies
# ON companies.id = clients.company_id AND companies.version_id IS NULL

Как видите, автоматически включается Company default_scope. Итак, я попробовал это:

Company.unscoped { Client.joins(:company) }.to_sql
# SELECT clients.* FROM clients INNER JOIN companies
# ON companies.id = clients.company_id AND companies.version_id IS NULL

Опять же, я получил тот же результат, даже при использовании unscoped с блоком.

Затем я решил добавить новую модель в модель с областью действия unscoped:

class Client
  belongs_to :company
  belongs_to :unscoped_company, -> { unscoped }, foreign_key: :company_id, class_name: "Company"
end

Добавив это, я дал еще одну попытку:

Client.joins(:unscoped_company).to_sql
# SELECT clients.* FROM clients INNER JOIN companies
# ON companies.id = clients.company_id AND companies.version_id IS NULL

И все же прицел применяется.

Знаете ли вы, как я могу успешно объединить обе таблицы без применения этого default_scope? Удаление этого default_scope не вариант, так как это большое приложение и изменение, которое потребует слишком много времени.


Rails v4.2.7

Ruby v2.2.3

Ответы [ 5 ]

2 голосов
/ 01 июня 2019

Я провел некоторые исследования, не найдя ни одного прямого решения.

Вот пара обходных путей.Я не могу сказать, будут ли они работать в ваших цепных соединениях.


Первый базовый, сделайте это вручную:
Client.joins("INNER JOINS companies ON companies.id = clients.company_id).to_sql


Другая опция определяет класс CompanyUnscoped, который наследуется от Company, удаляя область по умолчанию:
class CompanyUnscoped < Company

  self.default_scopes = []

end

Не забудьте добавить эту строку в класс клиента:

belongs_to :company_unscoped, foreign_key: :company_id

Тогда вы сможете позвонить

Client.joins(:company_unscoped)
#=> SELECT "clients".* FROM "clients" INNER JOIN "companies" ON "companies"."id" = "clients"."company_id"
0 голосов
/ 03 июня 2019

Похоже, что нет простого решения, которое вы ищете.Есть несколько тем для обсуждения.

https://github.com/rails/rails/issues/20679

https://github.com/rails/rails/issues/20011

Но мне понравился способ, предложенный @iGian.

0 голосов
/ 03 июня 2019

Вы можете попробовать https://api.rubyonrails.org/classes/ActiveRecord/QueryMethods.html#method-i-unscope, но я бы ожидал, что https://api.rubyonrails.org/classes/ActiveRecord/Scoping/Default/ClassMethods.html#method-i-unscoped сработает. Вы можете попробовать это снова без блока, позволяя ему прийти после применения нежелательной области действия.

0 голосов
/ 01 июня 2019

Применить непосредственно как метод класса

Client.unscoped.joins(:company).to_sql
0 голосов
/ 01 июня 2019

Вы можете сделать это вручную:

Client.joins('inner join company on companies.id = clients.company_id')
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...