You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rails:如何根据current_user类型过滤AccountStatement关联记录?

可行实现方案

方法1:动态关联 + 线程变量传递当前用户

先在全局控制器中把当前用户存入线程变量,确保模型能安全访问:

# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  before_action :set_current_user
  after_action :clear_current_user

  private

  def set_current_user
    Thread.current[:current_user] = current_user
  end

  def clear_current_user
    Thread.current[:current_user] = nil
  end
end

然后修改Client和Account的关联定义,用lambda动态过滤结果:

# app/models/client.rb
class Client < ApplicationRecord
  has_many :accounts
  has_many :account_statements, lambda {
    current_user = Thread.current[:current_user]
    if current_user.is_a?(Client) && current_user.id == id
      where(auto_generated: false)
    end
  }
end

# app/models/account.rb
class Account < ApplicationRecord
  belongs_to :client
  has_many :account_statements, lambda {
    current_user = Thread.current[:current_user]
    if current_user.is_a?(Client) && current_user.id == client_id
      where(auto_generated: false)
    end
  }
end

这样调用client.account_statements或account.account_statements时,会自动根据当前用户身份过滤:登录用户是对应Client时,只返回非自动生成的账单;Admin用户则返回全部数据。

方法2:封装通用作用域,显式传参调用

如果不想依赖线程上下文,可以在AccountStatement模型中定义带参数的作用域,在业务逻辑中手动传入当前用户:

# app/models/account_statement.rb
class AccountStatement < ApplicationRecord
  belongs_to :client
  belongs_to :account

  scope :visible_to, ->(user) {
    if user.is_a?(Client)
      where(auto_generated: false, client_id: user.id)
    else
      all
    end
  }
end

在控制器中调用时:

# Client账单列表页
def index
  @client = Client.find(params[:id])
  @account_statements = @client.account_statements.visible_to(current_user)
end

# Account账单详情页
def show
  @account = Account.find(params[:id])
  @account_statements = @account.account_statements.visible_to(current_user)
end

这种方式更显式,适合对模型上下文依赖有严格要求的场景。

方法3:覆盖关联默认方法,保持调用方式不变

如果希望外部调用account_statements的代码完全不用修改,可以重命名原始关联,再自定义同名方法做过滤:

# app/models/client.rb
class Client < ApplicationRecord
  has_many :accounts
  has_many :raw_account_statements, class_name: 'AccountStatement'

  def account_statements
    current_user = Thread.current[:current_user]
    if current_user.is_a?(Client) && current_user.id == id
      raw_account_statements.where(auto_generated: false)
    else
      raw_account_statements
    end
  end
end

# app/models/account.rb
class Account < ApplicationRecord
  belongs_to :client
  has_many :raw_account_statements, class_name: 'AccountStatement'

  def account_statements
    current_user = Thread.current[:current_user]
    if current_user.is_a?(Client) && current_user.id == client_id
      raw_account_statements.where(auto_generated: false)
    else
      raw_account_statements
    end
  end
end

外部调用时依然用client.account_statements,完全感知不到内部的过滤逻辑。

优化:用Concern复用过滤逻辑

为避免Client和Account模型代码重复,可以把过滤逻辑封装到Concern中:

# app/models/concerns/account_statement_visibility.rb
module AccountStatementVisibility
  extend ActiveSupport::Concern

  included do
    has_many :raw_account_statements, class_name: 'AccountStatement'
  end

  def account_statements
    current_user = Thread.current[:current_user]
    target_id = respond_to?(:client_id) ? client_id : id
    if current_user.is_a?(Client) && current_user.id == target_id
      raw_account_statements.where(auto_generated: false)
    else
      raw_account_statements
    end
  end
end

然后在模型中引入即可:

class Client < ApplicationRecord
  include AccountStatementVisibility
  # 其他代码
end

class Account < ApplicationRecord
  include AccountStatementVisibility
  # 其他代码
end

内容的提问来源于stack exchange,提问作者SomeSchmo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 21:18:39