如何在整个 Rails 活动记录关联链中取消特定模型的默认范围?

Posted

技术标签:

【中文标题】如何在整个 Rails 活动记录关联链中取消特定模型的默认范围?【英文标题】:How to unscope the default scope for the specific model in the whole rails active record association chain? 【发布时间】:2017-08-26 00:23:18 【问题描述】:

我正在尝试解决以下问题。我有三个模型:User has_many Camera has_many Recordings。

class AddTables < ActiveRecord::Migration
  def change
    create_table :users
    create_table :cameras do |t|
      t.references :user, null: false, foreign_key: true
      t.timestamp :archived_at, index: true
    end
    create_table :recordings do |t|
      t.references :camera, null: false, foreign_key: true
      t.timestamp :archived_at, index: true
    end
  end
end

相机和记录具有特殊字段“archived_at”以将记录标记为已删除(软删除)。我希望 User#recordings 浏览所有摄像机(任何 Camera.ar​​chived_at),但同时,我希望它只浏览非归档记录(Recording.archived_at == nil)。我尝试了几个案例,但都没有成功。

=== 案例 1 ===

class User < ActiveRecord::Base
  has_many :cameras, ->  unscope where: :archived_at 
  has_many :recordings, through: :cameras
end

class Camera < ActiveRecord::Base
  default_scope  where(archived_at: nil) 

  belongs_to :user
  has_many :recordings
end

class Recording < ActiveRecord::Base
  default_scope  where(archived_at: nil) 

  belongs_to :camera
end

irb(main):013:0> 重新加载!; User.first.recordings(true) 正在重新加载... 用户负载 (0.1ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT 1 记录负载(0.2ms)选择“记录”。* FROM“记录”INNER JOIN“相机”在“记录”中。“camera_id”=“相机”。“id”在哪里“相机”。“user_id”=$ 1 [[”用户 ID", 1]] => #]>

=== 案例 2 ===

class User < ActiveRecord::Base
  has_many :cameras, ->  unscope where: :archived_at 
  has_many :recordings, through: :cameras
end

class Camera < ActiveRecord::Base
  default_scope  where(cameras: archived_at: nil) 

  belongs_to :user
  has_many :recordings
end

class Recording < ActiveRecord::Base
  default_scope  where(recordings: archived_at: nil) 

  belongs_to :camera
end

irb(main):013:0> 重新加载!; User.first.recordings(true) 正在重新加载... 用户负载 (0.1ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT 1 记录负载(0.2ms)选择“记录”。* FROM“记录”INNER JOIN“相机”在“记录”中。“camera_id”=“相机”。“id”在哪里“相机”。“user_id”=$ 1 [[”用户 ID", 1]] => #]> irb(main):014:0> 重新加载!; User.first.recordings(true) 正在重新加载... 用户负载 (0.1ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT 1 记录负载(0.2 毫秒)选择“记录”。*从“记录”INNER JOIN“相机”打开“记录”。“camera_id”=“相机”。“id”其中“记录”。“归档_at”为空且“相机” "."archived_at" 为 NULL AND "cameras"."user_id" = $1 [["user_id", 1]] => #]>

=== 案例 3 ===

class User < ActiveRecord::Base
  has_many :cameras, ->  unscope where: cameras: :archived_at 
  has_many :recordings, through: :cameras
end

class Camera < ActiveRecord::Base
  default_scope  where(cameras: archived_at: nil) 

  belongs_to :user
  has_many :recordings
end

class Recording < ActiveRecord::Base
  default_scope  where(recordings: archived_at: nil) 

  belongs_to :camera
end

irb(main):016:0> 重新加载!; User.first.recordings(true) 正在重新加载... 用户负载 (0.2ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT 1 记录负载(0.2 毫秒)选择“记录”。*从“记录”INNER JOIN“相机”打开“记录”。“camera_id”=“相机”。“id”其中“记录”。“归档_at”为空且“相机” "."archived_at" 为 NULL AND "cameras"."user_id" = $1 [["user_id", 1]] => #]>

【问题讨论】:

【参考方案1】:

试试这个:

user.rb:

class User < ApplicationRecord
    has_many :cameras
    has_many :recordings, ->  unscope(where: :archived_at).where(recordings: archived_at: nil) , through: :cameras
end

camera.rb:

class Camera < ApplicationRecord
    default_scope  where(archived_at: nil) 
    belongs_to :user
    has_many :recordings
end

recording.rb:

class Recording < ApplicationRecord
    belongs_to :camera
    default_scope  where(archived_at: nil) 
end

这是输出查询:

irb(main):088:0> User.first.recordings
User Load (0.2ms)  SELECT  "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ?  [["LIMIT", 1]]
Recording Load (0.1ms)  SELECT "recordings".* FROM "recordings" INNER JOIN "cameras" ON "recordings"."camera_id" = "cameras"."id" WHERE "cameras"."user_id" = ? AND "recordings"."archived_at" IS NULL  [["user_id", 1]]

【讨论】:

但缺点是我们必须为每个使用 Recording 的关联添加这样的范围 是的,没错。但是为了缓解这个缺点,您可以创建包含记录关联的关注点,并将其包含在每个模型中。

以上是关于如何在整个 Rails 活动记录关联链中取消特定模型的默认范围?的主要内容,如果未能解决你的问题,请参考以下文章

Rails引用相同模型的活动记录关联

如何将活动记录类特定属性映射到rails中相应的通用关注方法

如何要求在 Rails 之外工作的活动记录

如何在 Rails 中使用延迟作业取消预定作业?

Rails:返回记录关联模型的ID数组全部存在的记录

在 Rails 中找不到关联问题