我在之前只有一个Devise模型的应用程序中有一些Devise的基本经验。但是,我从头开始重写它以扩展功能,在第二次迭代中,我认为最好有两个独立的用户模型(patient_user和staff_user)。
我知道CanCan和Rolify,并将使用其中一个模型而不是其他模型。
我的问题是设置after_sign_in_path_for并将每个模型重定向到不同的“主屏幕”。
我已将每个模型设置为单独的after_sign_up_path,并且效果很好。
class RegistrationsController < Devise::RegistrationsController
protected
# Creating separate after_sign_up_paths for patient_user and staff_user
def after_sign_up_path_for(patient_user)
flash[:notice] = 'Welcome! You have signed up successfully.'
privacy_agreement_path
end
# Add an after_sign_up path for staff_user
def after_sign_up_path_for(staff_user)
flash[:notice] = 'Welcome! You have signed up successfully.'
dashboard_path
end
end
显然,after_sign_in_path_for应该在Application Controller中定义,而不是在Sessions Controller中定义。
Stack Overflow question clarifying this difference
这是我最好的尝试:
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
def after_sign_in_path_for(resource)
case resource
when patient_user
privacy_agreement_path
when staff_user
dashboard_path
end
end
end
给出错误:
undefined local variable or method `patient_user' for #<Devise::SessionsController:0x00000109a40e48>
如果我将案例选择条件大写,那么它似乎识别出变量,但我得到一个完全不同的错误:
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
def after_sign_in_path_for(resource)
case resource
when Patient_user
privacy_agreement_path
when Staff_user
dashboard_path
end
end
end
给出错误
Circular dependency detected while autoloading constant Patient_user (RuntimeError)
./app/controllers/application_controller.rb:11:in `after_sign_in_path_for'
我已经尝试了很多Google搜索并查看了其他各种Devise和Circular Dependency问题,但未能找到解决办法,我想我在Devise上不够好,不知道我在做什么。< / p>
我尝试的另一件事是在应用程序控制器中调用after_sign_in_path_for后,使patient_user和staff_user分开
#application_controller.rb
def after_sign_in_path_for(patient_user)
privacy_agreement_path
end
def after_sign_in_path_for(staff_user)
dashboard_path
end
这适用于staff_user,但是转到/ patient_users / sign_in并提供有效的用户名和密码,而是重定向到dashboard_path(而不是privacy_agreement_path)。
该问题似乎集中在使用“资源”和旨在将“patient_user”帐户重定向到“privacy_agreement_path”和“staff_user”帐户为“dashboard_path”的条件语句。这适用于RegistrationsController中的after_sign_up_path,但不适用于ApplicationController中的after_sign_in_path。
其他档案
#routes.rb
devise_for :patient_users, :controllers => { :registrations => :registrations }
devise_for :staff_users, :controllers => { :registrations => :registrations }
-
#config/initializers/devise.rb
# https://stackoverflow.com/questions/8320398/second-devise-model-not-using-generated-views
# Workaround for having multiple Devise models, used the second answer
config.scoped_views = true
非常感谢任何帮助。
修改
我尝试了Vapire的解决方案:
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
def after_sign_in_path_for(resource)
# check for the class of the object to determine what type it is
case resource.class
when PatientUser
privacy_agreement_path
when StaffUser
dashboard_path
end
end
end
但这引发了错误:
undefined method `staff_user_url' for #<Devise::SessionsController:0x000001047a0198> (NoMethodError)
通过谷歌搜索,我发现this discussion on Devise github,对于这是某种错误还是仅仅执行不力,完全没有达成共识。
我遵循建议的解决方案,即更新routes.rb
#routes.rb
devise_for :patient_users, :controllers => { :registrations => :registrations }
devise_for :staff_users, :controllers => { :registrations => :registrations }
resources :patient_users #added as bugfix
resources :staff_users #added as bug fix
这给出了一个新错误:
uninitialized constant StaffUsersController (ActionController::RoutingError)
所以我创建了一个新的Controller文件:
#controllers/staff_users_controller.rb
class StaffUsersController < ApplicationController
end
给出了错误
The action 'show' could not be found for StaffUsersController (AbstractController::ActionNotFound)
所以我把它添加到控制器文件
#controllers/staff_users_controller.rb
class StaffUsersController < ApplicationController
def show
end
end
当然这引发了这个错误:
Missing template staff_users/show, application/show with {:locale=>[:en], :formats=>[:html], :handlers=>[:erb, :builder, :raw, :ruby, :jbuilder, :coffee]}. Searched in:
所以我也添加了该文件(只是app/views/staff_users.html.erb
处的空白文件)
然后可以使用,但重定向到错误的页面/staff_users/1
所以我再次修改了控制器
#controllers/staff_users_controller.rb
class StaffUsersController < ApplicationController
def show
redirect_to dashboard_path
end
end
然后一切正常。这似乎是一个非常复杂的解决方案,但是我必须为PatientUsers做同样的事情,而且我已经选择了许多我不需要的备用资源路径。
编辑2:
Mandeep要求的调试器信息。
[7, 16] in /Users/Me/Code/medapp/app/controllers/application_controller.rb
7 # https://github.com/plataformatec/devise/wiki/How-To%3A-Redirect-to-a-specific-page-on-successful-sign-in-and-sign-out
8 # redirect successfully signed in users to the dashboard
9 def after_sign_in_path_for(resource)
10 debugger
11 # check for the class of the object to determine what type it is
=> 12 case resource.class
13 when PatientUser
14 privacy_agreement_path
15 when StaffUser
16 dashboard_path
(rdb:2) resource.show
*** NoMethodError Exception: undefined method `show' for #<PatientUser:0x0000010171c1c0>
(rdb:2) @resource = resource
#<PatientUser id: 2, email: "patient_user@example.com", encrypted_password: "$2a$10$qY0jBEC8UZHD883ryq69BevPo5oxV.9LPDM8K44gXqcD...", reset_password_token: nil, reset_password_sent_at: nil, remember_created_at: nil, sign_in_count: 16, current_sign_in_at: "2014-07-03 08:46:07", last_sign_in_at: "2014-07-03 08:45:06", current_sign_in_ip: "127.0.0.1", last_sign_in_ip: "127.0.0.1", created_at: "2014-07-03 03:05:01", updated_at: "2014-07-03 08:46:07">
似乎与我的预期完全一样,应该表明resource.class可以在没有任何大的变通方法的情况下正常工作,但显然不是。
解决方案
所以只需使用If条件而不是大小写条件修复整个事物,不需要任何其他东西。我不知道为什么会这样,但这是一个充分的解决方案。
#registrations_controller.rb
class RegistrationsController < Devise::RegistrationsController
protected
# BUGFIX
# https://stackoverflow.com/questions/19451881/devise-after-sign-in-path-for-works-but-not-the-other-ones
# Creating separate after_sign_up_paths for patient_user and staff_user
def after_sign_up_path_for(resource)
# check for the class of the object to determine what type it is
if resource.class == PatientUser
privacy_agreement_path
elsif resource.class == StaffUser
dashboard_path
end
end
end
和
#application_controllers.rb
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
def after_sign_in_path_for(resource)
# check for the class of the object to determine what type it is
if resource.class == PatientUser
privacy_agreement_path
elsif resource.class == StaffUser
dashboard_path
end
end
end
感谢Mandeep和Vapire的帮助!!
答案 0 :(得分:7)
鉴于你的两个用户模型都被称为PatientUser
和StaffUser
你应该这样做:
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
def after_sign_in_path_for(resource)
# check for the class of the object to determine what type it is
case resource.class
when PatientUser
privacy_agreement_path
when StaffUser
dashboard_path
end
end
end
详细说明你的其他尝试:
在Devise为您登录用户后,它只需向您的应用程序控制器拨打after_sign_in_path(resource)
。无论是调用Devise本身提供的标准实现还是自定义实现,对于Devise来说都不是问题。如果您自己实现它,它将为您完全做些什么。因此,如果你想对它做出反应,你有责任检查哪种类型作为资源参数。
# application_controller.rb
# you implement the method once
def after_sign_in_path_for(patient_user)
privacy_agreement_path
end
# you implement it twice which means
# this overrides the above method, so a call to after_sign_in_path will always
# result in calling this method, no matter what type of user it is
def after_sign_in_path_for(staff_user)
dashboard_path
end
仅仅因为您以某种方式命名参数,它不会提供有关参数类型的信息。这不是OO编程的设计方式。