Rails 如何在任何 model 中获取 Devise 的 current_user 信息?

OrderSun · February 24, 2018 · Last by n5ken replied at February 27, 2018 · 3481 hits

方法如下:

module ActiveRecordExtension                                                                              
  def current_passport
    Thread.current[:passport]
  end

  def current_passport=(value)
    Thread.current[:passport] = value
  end

  def puts_sth
    puts ActiveRecordExtension.current_passport.username
  end

  module_function :current_passport, :current_passport=
end                                                                                                       

ActiveRecord::Base.send :include, ActiveRecordExtension 

今天看 DHH 发的视频也发现,basecamp 的项目在 model 里也有 Current,原来是这样实现的。

Reply to jasl

请问为什么不要这样做,会引发什么问题?

Reply to OrderSun

是一种流派,但是另一派认为是坏的实践(我站这边)

简而言之,我不赞同让请求上下文污染到模型,模型的职责已经很多了

最好不这样做,但一定要的话 参考 https://github.com/steveklabnik/request_store

最后大概是这个样子:

module ActiveRecordExtension   
  class << self                
    attr_accessor :current_passport 
  end

  def puts_sth                 
    puts ActiveRecordExtension.current_passport.username    
  end
end

ActiveRecord::Base.send :include, ActiveRecordExtension

controller 中添加 before_action :set_current_passport

def set_current_passport
  ActiveRecordExtension.current_passport = current_passport
end

感谢各位大神的帮助。

Reply to OrderSun

这个实现线程不安全,在基于线程的 app server 上会出问题,例如 puma。

Reply to Rei

改成这个样子就可以了:

module ActiveRecordExtension                                                                              
  def current_passport
    Thread.current[:passport]
  end

  def current_passport=(value)
    Thread.current[:passport] = value
  end

  def puts_sth
    puts ActiveRecordExtension.current_passport.username
  end

  module_function :current_passport, :current_passport=
end                                                                                                       

ActiveRecord::Base.send :include, ActiveRecordExtension 

楼主是不是在做多租户系统?根据当前用户通过模型自动对数据查询进行筛选?

Reply to OrderSun

这个实现要小心如果某个请求没有设值,会拿到上个请求遗留的值。

Reply to OrderSun

你应该在使用完一次线程变量的最后,将这个线程变量设为 nil,防止拿到上一次遗留的值

Reply to OrderSun

7 楼才是正解,少年😏

原理也是 Thread.current,不过在 Request 进来时设置,Request 完成时清除!这是关键!具体可以看那个 gem 都代码!少年

Reply to n5ken

不是啊,我就是好奇,想研究一下😌

Reply to pathbox

多谢指正,再添加一个 clear_passport! 方法就 OK 了。

Reply to tommy__

多谢指正👍 👍 👍

Reply to OrderSun

好吧。。那你用 request_store 就好了

You need to Sign in before reply, if you don't have an account, please Sign up first.