【问题标题】:Rails: Using Devise with single table inheritanceRails:使用带有单表继承的设计
【发布时间】:2011-08-07 02:51:18
【问题描述】:

我在让 Devise 以我希望的单表继承方式工作时遇到问题。

我有两种不同类型的帐户,如下所示:

class Account < ActiveRecord::Base
  devise :database_authenticatable, :registerable
end

class User < Account
end

class Company < Account
end

我有以下路线:

devise_for :account, :user, :company

用户在/user/sign_up注册,公司在/company/sign_up注册。所有用户都使用/account/sign_in 的单一表单登录(Account 是父类)。

但是,通过此表单登录似乎只能在 Account 范围内对它们进行身份验证。对 /user/edit/company/edit 等操作的后续请求会将用户定向到相应范围的登录屏幕。

如何让 Devise 识别帐户“类型”并在相关范围内对其进行身份验证?

非常感谢任何建议。

【问题讨论】:

    标签: ruby-on-rails ruby-on-rails-3 devise single-table-inheritance


    【解决方案1】:

    有一种简单的方法可以在路由中处理 STI。

    假设您有以下 STI 模型:

    def Account < ActiveRecord::Base
    # put the devise stuff here
    devise :database_authenticatable, :registerable,
        :recoverable, :rememberable, :trackable, :validatable
    end
    
    def User < Account
    end
    
    def Company < Account
    

    一个经常被忽略的方法是,你可以在你的 routes.rb 文件中的认证方法中指定一个块:

    ## config/routes.rb
    
    devise_for :accounts, :skip => :registrations
    devise_for :users, :companies, :skip => :sessions
    
    # routes for all users
    authenticated :account do
    end
    
    # routes only for users
    authenticated :user, lambda {|u| u.type == "User"} do
    end
    
    # routes only for companies
    authenticated :user, lambda {|u| u.type == "Company"} do
    end
    

    获取各种帮助方法,例如“current_user”和“authenticate_user!” (“current_account”和“authenticate_account!”已经定义)无需为每个定义单独的方法(随着更多用户类型的添加,这很快变得无法维护),您可以在 ApplicationController 中定义动态帮助方法:

    ## controllers/application_controller.rb
    def ApplicationController < ActionController::Base
      %w(User Company).each do |k| 
        define_method "current_#{k.underscore}" do 
            current_account if current_account.is_a?(k.constantize)
        end 
    
        define_method "authenticate_#{k.underscore}!" do 
        |opts={}| send("current_#{k.underscore}") || not_authorized 
        end 
      end
    end
    

    这就是我解决 rails devise STI 问题的方法。

    【讨论】:

    • 在这行代码中不应该是 :user 是 :company 吗? ``` # 路由仅适用于经过身份验证的公司 :user, lambda {|u| u.type == "Company"} 结束 ``
    【解决方案2】:

    我刚刚遇到了问题中概述的确切场景(更改了类名)。这是我的解决方案(设计 2.2.3,Rails 3.2.13):

    在 config/routes.rb 中:

    devise_for :accounts, :controllers => { :sessions => 'sessions' }, :skip => :registrations
    devise_for :users, :companies, :skip => :sessions
    

    在 app/controllers/sessions_controller.rb 中:

    class SessionsController < Devise::SessionsController
        def create
            rtn = super
            sign_in(resource.type.underscore, resource.type.constantize.send(:find, resource.id)) unless resource.type.nil?
            rtn
        end
    end
    

    注意:由于您的 Accounts 类仍然是 :registerable,views/devise/shared/_links.erb 中的默认链接将尝试发出,但 new_registration_path(Accounts) 将不起作用(我们:在路由中跳过它绘图)并导致错误。您必须生成设计视图并手动删除它。

    https://groups.google.com/forum/?fromgroups=#!topic/plataformatec-devise/s4Gg3BjhG0E 致敬,为我指明了正确的方向。

    【讨论】:

    • 我使用了这个并且我得到了错误 NoMethodError (undefined method 'type' ... 在行 sign_in(resource.type ... 我需要做什么来改变这个?
    • 我猜只是在名为 type 的帐户中创建一个字符串列?并将其设置为模型名称?
    • 这也是我的猜测。 ActiveRecord STI 期待这一点。
    • 我试过了,但我的应用程序仍然因注释中提到的 new_registration_path 问题而崩溃。由于所有的设计视图都使用通用资源路径,我如何手动删除 new_registration_path(Accounts)?
    【解决方案3】:

    尝试像这样更改路由:
    devise_for :accounts, :users, :companies
    因为 Devise 使用复数名称来表示它的资源

    如果对你有帮助,请告诉我

    【讨论】:

    • 感谢您的建议。不幸的是,这并没有解决问题。通过/accounts/sign_in 登录仍然不允许我访问/users/edit/companies/edit
    【解决方案4】:

    如果不覆盖会话控制器,我认为这是不可能的。每个登录页面都有一个特定的范围,设计将根据您的路由定义进行身份验证。

    通过使用您的路由文件中的 devise_scope 函数来强制 :users 和 :companies 使用相同的登录页面(可以找到操作方法@ 987654321@),但我很确定您必须修改会话控制器以执行一些自定义逻辑,以确定正在登录的用户类型。

    【讨论】:

    • @gjb 你是如何处理这个问题的?您找到更好的解决方案了吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-08
    • 1970-01-01
    • 2021-10-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多