Vap*_*ire 34 ruby-on-rails multiple-models devise
首先,我与谷歌和雅虎进行了密切的搜索,我发现了一些关于像我这样的主题的回复,但它们并没有真正涵盖我需要知道的内容.
我的应用程序中有几个用户模型,现在它是客户,设计师,零售商,似乎还有更多未来.它们都有不同的数据存储在他们的表格中,以及他们允许或不允许的网站上的几个区域.所以我想去设计+ CanCan的方式,并尝试我的运气与多态关联,所以我得到以下模型设置:
class User < AR
belongs_to :loginable, :polymorphic => true
end
class Customer < AR
has_one :user, :as => :loginable
end
class Designer < AR
has_one :user, :as => :loginable
end
class Retailer < AR
has_one :user, :as => :loginable
end
Run Code Online (Sandbox Code Playgroud)
对于注册,我已经为每个不同的用户类型定制了视图,我的路由设置如下:
devise_for :customers, :class_name => 'User'
devise_for :designers, :class_name => 'User'
devise_for :retailers, :class_name => 'User'
Run Code Online (Sandbox Code Playgroud)
现在注册控制器是标准的(这是"设计/注册"),但我想,因为我有不同的数据存储在不同的模型中,我也必须自定义这种行为!?
但与此设置我喜欢助手customer_signed_in?和designer_signed_in?,但我真正需要的是一个一般的助手一样user_signed_in?的可以访问的所有用户在网站上的区域,无论哪个用户类型.
我也喜欢路线助手new_user_session_path而不是几个new_*type*_session_path等等.事实上,我需要做的就是注册过程......
所以我想知道如果这是解决这个问题的方法??? 或者是否有更好/更容易/更少必须定制的解决方案?
罗伯特,提前谢谢
Vap*_*ire 35
好的,所以我完成了它并找到了以下解决方案.
我需要一点点设计,但它并不复杂.
用户模型
# user.rb
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
attr_accessible :email, :password, :password_confirmation, :remember_me
belongs_to :rolable, :polymorphic => true
end
Run Code Online (Sandbox Code Playgroud)
客户模型
# customer.rb
class Customer < ActiveRecord::Base
has_one :user, :as => :rolable
end
Run Code Online (Sandbox Code Playgroud)
Designer模型
# designer.rb
class Designer < ActiveRecord::Base
has_one :user, :as => :rolable
end
Run Code Online (Sandbox Code Playgroud)
因此,User模型具有简单的多态关联,定义它是Customer还是Designer.
我接下来要做的就是生成设计视图,rails g devise:views使其成为我的应用程序的一部分.由于我只需要定制注册,我只保留app/views/devise/registrations文件夹并删除其余部分.
然后我为新注册定制了注册视图,可以在app/views/devise/registrations/new.html.erb生成它们之后找到它们.
<h2>Sign up</h2>
<%
# customized code begin
params[:user][:user_type] ||= 'customer'
if ["customer", "designer"].include? params[:user][:user_type].downcase
child_class_name = params[:user][:user_type].downcase.camelize
user_type = params[:user][:user_type].downcase
else
child_class_name = "Customer"
user_type = "customer"
end
resource.rolable = child_class_name.constantize.new if resource.rolable.nil?
# customized code end
%>
<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f| %>
<%= my_devise_error_messages! # customized code %>
<div><%= f.label :email %><br />
<%= f.email_field :email %></div>
<div><%= f.label :password %><br />
<%= f.password_field :password %></div>
<div><%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation %></div>
<% # customized code begin %>
<%= fields_for resource.rolable do |rf| %>
<% render :partial => "#{child_class_name.underscore}_fields", :locals => { :f => rf } %>
<% end %>
<%= hidden_field :user, :user_type, :value => user_type %>
<% # customized code end %>
<div><%= f.submit "Sign up" %></div>
<% end %>
<%= render :partial => "devise/shared/links" %>
Run Code Online (Sandbox Code Playgroud)
对于每个用户类型,我创建了一个单独的部分,其中包含特定用户类型的自定义字段,即Designer - > _designer_fields.html
<div><%= f.label :label_name %><br />
<%= f.text_field :label_name %></div>
Run Code Online (Sandbox Code Playgroud)
然后我设置路由以便在注册时使用自定义控制器
devise_for :users, :controllers => { :registrations => 'UserRegistrations' }
Run Code Online (Sandbox Code Playgroud)
然后,我产生了控制器来处理定制的登记过程中,复制从原始源代码create的方法Devise::RegistrationsController和修改它的工作我的方式(不要忘了你的视图文件移动到相应的文件夹,在我的情况app/views/user_registrations
class UserRegistrationsController < Devise::RegistrationsController
def create
build_resource
# customized code begin
# crate a new child instance depending on the given user type
child_class = params[:user][:user_type].camelize.constantize
resource.rolable = child_class.new(params[child_class.to_s.underscore.to_sym])
# first check if child instance is valid
# cause if so and the parent instance is valid as well
# it's all being saved at once
valid = resource.valid?
valid = resource.rolable.valid? && valid
# customized code end
if valid && resource.save # customized code
if resource.active_for_authentication?
set_flash_message :notice, :signed_up if is_navigational_format?
sign_in(resource_name, resource)
respond_with resource, :location => redirect_location(resource_name, resource)
else
set_flash_message :notice, :inactive_signed_up, :reason => inactive_reason(resource) if is_navigational_format?
expire_session_data_after_sign_in!
respond_with resource, :location => after_inactive_sign_up_path_for(resource)
end
else
clean_up_passwords(resource)
respond_with_navigational(resource) { render_with_scope :new }
end
end
end
Run Code Online (Sandbox Code Playgroud)
这基本上做的是控制器根据通过URL中的简单GET参数使用参数的视图中的隐藏字段user_type传递给控制器create方法的参数来确定必须创建哪个用户类型.
例如:
如果你去,/users/sign_up?user[user_type]=designer你可以创建一个设计师.
如果你去,/users/sign_up?user[user_type]=customer你可以创建一个客户.
该my_devise_error_messages!方法是一种辅助方法,它还基于原始devise_error_messages!方法处理关联模型中的验证错误
module ApplicationHelper
def my_devise_error_messages!
return "" if resource.errors.empty? && resource.rolable.errors.empty?
messages = rolable_messages = ""
if !resource.errors.empty?
messages = resource.errors.full_messages.map { |msg| content_tag(:li, msg) }.join
end
if !resource.rolable.errors.empty?
rolable_messages = resource.rolable.errors.full_messages.map { |msg| content_tag(:li, msg) }.join
end
messages = messages + rolable_messages
sentence = I18n.t("errors.messages.not_saved",
:count => resource.errors.count + resource.rolable.errors.count,
:resource => resource.class.model_name.human.downcase)
html = <<-HTML
<div id="error_explanation">
<h2>#{sentence}</h2>
<ul>#{messages}</ul>
</div>
HTML
html.html_safe
end
end
Run Code Online (Sandbox Code Playgroud)
更新:
为了能够支持类似的路由/designer/sign_up,/customer/sign_up您可以在路由文件中执行以下操作:
# routes.rb
match 'designer/sign_up' => 'user_registrations#new', :user => { :user_type => 'designer' }
match 'customer/sign_up' => 'user_registrations#new', :user => { :user_type => 'customer' }
Run Code Online (Sandbox Code Playgroud)
在内部路由语法中未使用的任何参数都会传递给params哈希.所以:user传递给params哈希.
就是这样了.通过这里和那里的一点点推文,我得到了一个非常通用的方式,这很容易扩展,许多其他用户模型共享一个共同的用户表.
希望有人发现它有用.
我没有找到任何方式评论接受的答案,所以我只想写在这里.
有些事情与接受的答案完全不同,可能是因为它已经过时了.
无论如何,我必须自己解决一些问题:
render_with_scope不再存在,只需使用render :newcreateRegistrationsController中的create函数中的第一行不符合规定.试试吧
# Getting the user type that is send through a hidden field in the registration form.
user_type = params[:user][:user_type]
# Deleting the user_type from the params hash, won't work without this.
params[:user].delete(:user_type)
# Building the user, I assume.
build_resource
Run Code Online (Sandbox Code Playgroud)而不是简单的build_resource.一些质量分配错误在未改变时出现.
class ApplicationController < ActionController::Base
protect_from_forgery
# Overriding the Devise current_user method
alias_method :devise_current_user, :current_user
def current_user
# It will now return either a Company or a Customer, instead of the plain User.
super.rolable
end
end
Run Code Online (Sandbox Code Playgroud)
render_with_scope| 归档时间: |
|
| 查看次数: |
14791 次 |
| 最近记录: |