简体   繁体   中英

Multiple user models with Ruby On Rails and devise to have separate registration but one common login

i have same problem just like this link:

Multiple user models with Ruby On Rails and devise to have separate registration routes but one common login route

in my app there are two model

  1. Company

  2. Employee

my User model

class User < ActiveRecord::Base
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
     :recoverable, :rememberable, :trackable, :validatable

  belongs_to :usr, :polymorphic => true
end

my Company model

class Company < ActiveRecord::Base
  has_many :users, :as => :usr
end

my Employee model

class Employee < ActiveRecord::Base
  has_many :users, :as => :usr
end

app/views/devise/registrations/new.html.erb

<h2>Sign up</h2>

<%
  # customized code begin

  params[:user][:user_type] ||= 'company'

  if ["company", "employee"].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 = "Company"
    user_type = "company"
  end

  resource.usr = child_class_name.constantize.new if resource.usr.nil?

  # customized code end
%>



<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
  <%= devise_error_messages! %>

  <div class="field">
    <%= f.label :email %><br />
    <%= f.email_field :email, autofocus: true %>
  </div>

  <div class="field">
    <%= f.label :password %>
    <% if @validatable %>
    <em>(<%= @minimum_password_length %> characters minimum)</em>
    <% end %><br />
    <%= f.password_field :password, autocomplete: "off" %>
  </div>

  <div class="field">
    <%= f.label :password_confirmation %><br />
    <%= f.password_field :password_confirmation, autocomplete: "off" %>
  </div>


  <% # customized code begin %>
  <%= fields_for resource.usr 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 class="actions">
    <%= f.submit "Sign up" %>
  </div>
<% end %>

<%= render "devise/shared/links" %>

my registration create method :

controllers/users/registrations_controller.rb

class Users::RegistrationsController < 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.usr = 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.usr.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 :new }
    end
  end
end

my application helper file :

module ApplicationHelper
    def my_devise_error_messages!
    return "" if resource.errors.empty? && resource.usr.errors.empty?

    messages = usr_messages = ""

    if !resource.errors.empty?
      messages = resource.errors.full_messages.map { |msg| content_tag(:li, msg) }.join
    end

    if !resource.usr.errors.empty?
      usr_messages = resource.usr.errors.full_messages.map { |msg| content_tag(:li, msg) }.join
    end

    messages = messages + usr_messages   
    sentence = I18n.t("errors.messages.not_saved",
                      :count => resource.errors.count + resource.usr.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

but i got error something like this when i open any user sign_up page:

localhost:3000/emloyees/sign_up **OR**

localhost:3000/companies/sign_up

error in registration view

ERROR :-

在此处输入图片说明

So what am I doing wrong?

Your question is really broad but to solve the error you posted, it is erroring undefined method because of the nested hash that doesn't exist. Here's the example:

[1] pry(main)> params = {}
=> {}
[2] pry(main)> params[:user]
=> nil
[3] pry(main)> params[:user][:user_type]
NoMethodError: undefined method `[]' for nil:NilClass

If your code is to expect that params[:user] won't ever exist without a user_type and you want to default that to company, then that would simply be:

[7] pry(main)> params[:user] ||= { user_type: "company" }
=> {:user_type=>"company"}
[8] pry(main)> params
=> {:user=>{:user_type=>"company"}}

Alternativley if you want to just return a string if params[:user] doesn't exist, you can use ||= or Hash#fetch with a default value. Hash#fetch is a safer way to error handle imo.

[5] pry(main)> params.fetch(:user, "company")
=> "company"

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM