如何取消连接ActiveRecord模型上的where子句

时间:2015-11-13 20:20:01

标签: ruby-on-rails ruby-on-rails-4 activerecord rails-activerecord

在我的模型MediaFileAsset上,我通过一个包含的lib添加了以下内容:

default_scope ->{eager_load(:service_asset_core).where(service_asset_cores: {archived: false})}

不考虑不使用default_scopes的原因,有没有办法使用unscope从查询中删除此范围?

所以,例如,也许有人通过了我

assets = MediaFileAsset.where(custom_conditions)

我希望将所有这些忽略archived service_asset_cores字段。如果archivedmedia_file_assets的字段,我可以使用

assets.unscope(where: :archived).count

但这不起作用,因为archivedServiceAssetCore上的字段,而不是MediaFileAsset

使用unscopedunscope(:where)工作来删除存档条件,但这些条件不仅仅删除了这个条件,这不是我正在寻找的条件。我相信这就是首先添加unscope()方法的原因,并且我试图了解是否可以使用它来删除已连接表格上的查询条件。

供参考:

2.2.2 > MediaFileAsset.unscoped.count
   (505.8ms)  SELECT COUNT(*) FROM `media_file_assets`
 => 3078951

为了解决具体的地方:: archived子句,我试过了:

2.2.2 > MediaFileAsset.unscope(where: {service_asset_cores: :archived}).count
   (0.5ms)  SELECT COUNT(DISTINCT `media_file_assets`.`id`) FROM `media_file_assets` LEFT OUTER JOIN `service_asset_cores` ON `service_asset_cores`.`service_asset_id` = `media_file_assets`.`id` AND `service_asset_cores`.`service_asset_type` = 'MediaFileAsset' WHERE `service_asset_cores`.`archived` = 0
 => 10

2.2.2 > MediaFileAsset.all.merge(ActsAsServiceAsset::ServiceAssetCore.unscoped).count
   (0.5ms)  SELECT COUNT(DISTINCT `media_file_assets`.`id`) FROM `media_file_assets` LEFT OUTER JOIN `service_asset_cores` ON `service_asset_cores`.`service_asset_id` = `media_file_assets`.`id` AND `service_asset_cores`.`service_asset_type` = 'MediaFileAsset' WHERE `service_asset_cores`.`archived` = 0
 => 10

2.2.2 > MediaFileAsset.unscope(where: :archived).count
   (0.8ms)  SELECT COUNT(DISTINCT `media_file_assets`.`id`) FROM `media_file_assets` LEFT OUTER JOIN `service_asset_cores` ON `service_asset_cores`.`service_asset_id` = `media_file_assets`.`id` AND `service_asset_cores`.`service_asset_type` = 'MediaFileAsset' WHERE `service_asset_cores`.`archived` =
Mysql2::Error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 1: SELECT COUNT(DISTINCT `media_file_assets`.`id`) FROM `media_file_assets` LEFT OUTER JOIN `service_asset_cores` ON `service_asset_cores`.`service_asset_id` = `media_file_assets`.`id` AND `service_asset_cores`.`service_asset_type` = 'MediaFileAsset' WHERE `service_asset_cores`.`archived` =
ActiveRecord::StatementInvalid: Mysql2::Error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 1: SELECT COUNT(DISTINCT `media_file_assets`.`id`) FROM `media_file_assets` LEFT OUTER JOIN `service_asset_cores` ON `service_asset_cores`.`service_asset_id` = `media_file_assets`.`id` AND `service_asset_cores`.`service_asset_type` = 'MediaFileAsset' WHERE `service_asset_cores`.`archived` =

2.2.2 > MediaFileAsset.all.merge(ActsAsServiceAsset::ServiceAssetCore.unscope(where: :archived)).count
# output omitted, same error as pervious

最后两个似乎是一个Rails(4.2.3)错误,因为生成的SQL无效。也许这就是我需要的地方,但这个错误在我的方式。还有其他方法我应该尝试,还是我坚持不可避免的default_scope的恐怖?

2 个答案:

答案 0 :(得分:1)

取消对指定表的作用域列(Rails 6.1 +)

从Rails 6.1开始,可以仅取消限制指定表中的列。

posts = Post.joins(:comments).group(:"posts.hidden")
posts = posts.where("posts.hidden": false, "comments.hidden": false)

posts.count
# => { false => 10 }

# unscope both hidden columns
posts.unscope(where: :hidden).count
# => { false => 11, true => 1 }

# unscope only comments.hidden column
posts.unscope(where: :"comments.hidden").count
# => { false => 11 }

这里是a link to the corresponding PR

所以,我想,以下可以解决您的问题:

MediaFileAsset.unscope(where: :"service_asset_cores.archived").where(custom_conditions)

答案 1 :(得分:0)

你非常接近。

MediaFileAsset.unscoped返回ActiveRecord::Relation,这意味着您可以在其上调用.count, .where, .includes,等。

所以完整的查询将是:

MediaFileAsset.unscoped.where(service_assset_cores: :archived).count

或者,您可以在MediaFileAsset上定义一个名为archived的命名范围,并在取消它后对其进行重新扫描:

class MediaFileAsset < ActiveRecord::Base
  scope :archived, { where service_asset_cores: :archived }
  # ...
end


MediaFileAsset.unscoped.archived.count
  def self.without_default_scope
    klass = self.dup
    klass.default_scopes = []
    klass
  end