Rails:使用具有单表继承的Devise

时间:2011-04-16 23:36:11

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

我遇到问题让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识别帐户'type'并对相关范围进行身份验证?

任何建议都非常感谢。

4 个答案:

答案 0 :(得分:15)

有一种简单的方法可以处理路线中的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文件中的authenticated方法中指定一个块:

## 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

这就是我解决轨道设计STI问题的方法。

答案 1 :(得分:10)

我刚刚遇到了问题中概述的确切场景(类名已更改)。这是我的解决方案(Devise 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类仍然是:可注册的,因此将尝试发出views / devise / shared / _links.erb中的默认链接,但new_registration_path(Accounts)将不起作用(我们:在路由中跳过它)绘图)并导致错误。您必须生成设计视图并手动删除它。

https://groups.google.com/forum/?fromgroups=#!topic/plataformatec-devise/s4Gg3BjhG0E提示,指出我正确的方向。

答案 2 :(得分:1)

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

通过使用路径文件中的devise_scope函数,可以对多个用户范围使用相同的sign_in页面,以强制:用户和:公司使用相同的登录页面(可以找到操作方法{ {3}}),但我非常肯定您必须修改会话控制器以执行一些自定义逻辑,以确定登录的用户类型。

答案 3 :(得分:0)

尝试改变路线,如下:
devise_for:accounts,:users,:companies
因为Devise为它的资源使用复数名称

如果有帮助,请告诉我