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

OrderSun · 2018年02月24日 · 最后由 n5ken 回复于 2018年02月27日 · 1017 次阅读

方法如下:

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 
共收到 18 条回复

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

jasl 回复

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

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

感谢各位大神的帮助。

OrderSun 回复

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

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 

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

OrderSun 回复

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

OrderSun 回复

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

OrderSun 回复

7楼才是正解,少年😏

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

n5ken 回复

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

pathbox 回复

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

tommy__ 回复

多谢指正👍 👍 👍

OrderSun 回复

好吧。。那你用 request_store 就好了

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册