web-dev-qa-db-ja.com

複数の「ユーザー」モデルを考案します

Rails 3.2とdevise 2.0を使用していますが、Railsを初めて使用します。

必要条件

私は次を達成したいと思います:

  • 2つ以上の「ユーザー」モデルがある。メンバー、顧客、管理者
  • すべてのモデルはいくつかの必須フィールドを共有します(例:電子メールとパスワード)
  • 各モデルにはいくつかの一意のフィールドがあります(例:顧客の会社のみ)
  • いくつかのフィールドは共有されているかもしれませんが、同じ検証を持っていません(例えば、名前は顧客には必須ですが、メンバーにはオプションです)
  • すべてのフィールドは登録プロセス中に入力する必要があるため、フォームは異なります
  • ログインフォームは一意である必要があります

可能な解決策

私はかなり長い間グーグルで検索してStackOverflowを検索しましたが、私には何も正しいとは思えません(私はJava guy、すみません:)で、今ではかなり混乱しています。2つの解決策が出てきました。

単一の工夫ユーザー

それが最もよくある答えです。デフォルトのdevise Userを作成し、Member-> UserとCustomer-> Userの間に関係を作成するだけです。ここでの懸念は、各モデルのカスタマイズされた登録プロセスをどのように達成できるかです。別のことを試してみましたが、すべてが混乱してしまいました!

複数のデバイスユーザー

これはカスタム登録プロセスを解決し、私には正しいようですが、ユニークなログインフォームはブロッカーです。 Devise :: Models :: Authenticatable.find_for_authentication(conditions)をオーバーライドするように提案するSO( Devise-2つのモデルからのログイン )で答えを見つけました。 (?)そして、私はRailsが初めてなので、それがうまくいくかどうか知りたいのですが?

アドバイスをしてくれてありがとう!

73
ddidier

行く方法を見つけましたが、これまでのところとても満足しています。他の人のためにここで説明します。

私は単一の「ユーザー」クラスで行った。私の問題は、各擬似モデルに対してカスタマイズされた登録プロセスを実現することでした。

model/user.rb:

class User < ActiveRecord::Base
  devise :confirmable,
       :database_authenticatable,
       :lockable,
       :recoverable,
       :registerable,
       :rememberable,
       :timeoutable,
       :trackable,
       :validatable

  # Setup accessible (or protected) attributes for your model
  attr_accessible :email, :password, :password_confirmation, :remember_me, :role

  as_enum :role, [:administrator, :client, :member]
  validates_as_enum :role
  ## Rails 4+ for the above two lines
  # enum role: [:administrator, :client, :member]

end

次に、 http://railscasts.com/episodes/217-multistep-formshttp://pastie.org/1084054 をオーバーライドして、2つの登録パスをオーバーライドしましたコントローラ:

config/routes.rb:

get  'users/sign_up'   => 'users/registrations#new',        :as => 'new_user_registration'

get  'clients/sign_up' => 'users/registrations#new_client', :as => 'new_client_registration'
post 'clients/sign_up' => 'users/registrations#create',     :as => 'client_registration'

get  'members/sign_up' => 'users/registrations#new_member', :as => 'new_member_registration'
post 'members/sign_up' => 'users/registrations#create',     :as => 'member_registration'

controllers/users/registrations_controller.rb:

各ステップで検証するフィールドを知っているウィザードクラスを作成しました

class Users::RegistrationsController < Devise::RegistrationsController

    # GET /resource/sign_up
    def new
        session[:user] ||= { }
        @user = build_resource(session[:user])
        @wizard = ClientRegistrationWizard.new(current_step)

        respond_with @user
    end

    # GET /clients/sign_up
    def new_client
        session[:user] ||= { }
        session[:user]['role'] = :client
        @user = build_resource(session[:user])
        @wizard = ClientRegistrationWizard.new(current_step)

        render 'new_client'
    end

    # GET /members/sign_up
    def new_member
      # same
    end

    # POST /clients/sign_up
    # POST /members/sign_up
    def create
        session[:user].deep_merge!(params[:user]) if params[:user]
        @user = build_resource(session[:user])
        @wizard = ClientRegistrationWizard.new(current_step)

        if params[:previous_button]
            @wizard.previous
        elsif @user.valid?(@wizard)
            if @wizard.last_step?
                @user.save if @user.valid?
            else
                @wizard.next
            end
        end

        session[:registration_current_step] = @wizard.current_step

        if @user.new_record?
            clean_up_passwords @user
            render 'new_client'
        else
            #session[:registration_current_step] = nil
            session[:user_params] = nil

            if @user.active_for_authentication?
                set_flash_message :notice, :signed_up if is_navigational_format?
                sign_in(:user, @user)
                respond_with @user, :location => after_sign_up_path_for(@user)
            else
                set_flash_message :notice, :"signed_up_but_#{@user.inactive_message}" if is_navigational_format?
                expire_session_data_after_sign_in!
                respond_with @user, :location => after_inactive_sign_up_path_for(@user)
            end
        end

    end

    private

    def current_step
        if params[:wizard] && params[:wizard][:current_step]
            return params[:wizard][:current_step]
        end
        return session[:registration_current_step]
    end

end

私の見解は次のとおりです。

  • new.rb
  • new_client.rbウィザードのステップに従ってパーシャルを含む:
    • _new_client_1.rb
    • _new_client_2.rb
  • new_member.rbウィザードのステップに従ってパーシャルを含む:
    • _new_member_1.rb
    • _new_member_2.rb
18
ddidier

Java guy =)にようこそ、Rails world。

  1. ユーザーごとに、データベースと対応するモデルにテーブルを作成します。
  2. データベースに単一のテーブルを作成し、ユーザータイプごとにモデルを作成します。これは、単一テーブル継承(STI)と呼ばれます。

どちらを選ぶべきですか?ロールの共通属性に依存します。それらがほぼ一般的で(たとえば、すべてに名前、メール、モバイルなどがある)、いくつかの属性が異なる場合は、STIソリューションを強くお勧めします。

STIの実行方法1.コマンド_Rails generate devise User_を使用してdeviseユーザーモデルとテーブルを作成します。2.移行を使用して、データベースのユーザーテーブルに文字列データ型のtypeという名前の列を追加します。 3.ユーザータイプごとにモデルを作成します(たとえば_Rails g model admin_)4. Adminクラスがユーザーモデルを継承するようにします

_class Admin < User
end
_

これで終わりです=)... Yupeee

管理者を作成するには、コマンドAdmin.create(...)を実行します。ここで、ドットは管理者の属性(電子メール、名前など)です...

これは 質問 でも役立つと思います

66
mohamagdy

私はあなたと同じような立場にあります。あらゆる種類のアプローチを試した後、私は単一のユーザーモデルを使用しました。これはポリモーフィックロールに属します。これは、シングルログインを実現する最も簡単な方法のようです。

ユーザーモデルには、ログイン専用の情報が含まれます。

役割モデルは、各役割に固有のフィールドと、その役割に固有の他の関連付けを保存します。

新しい登録は、個々のコントローラーを介してユーザータイプ(ロール)ごとにカスタマイズされ、ユーザーのネストされた属性を構築します。

class User < ActiveRecord::Base
    #... devise code ...
    belongs_to :role, :polymorphic => true
end

class Member < ActiveRecord::Base
    attr_accessible :name, :tel, :city  #etc etc....
    attr_accessible :user_attributes #this is needed for nested attributes assignment

    #model specific associations like  
    has_many :resumes

    has_one :user, :as => :role, dependent: :destroy
    accepts_nested_attributes_for :user
end 

ルート-メンバーモデルの単なる通常のもの。

resources :members
#maybe make a new path for New signups, but for now its new_member_path

コントローラー-ネストされた属性にはbuild_userが必要です

#controllers/members_controller.rb
def new
    @member = Member.new
    @member.build_user
end

def create
    #... standard controller stuff
end

views/members/new.html.erb

<h2>Sign up for new members!</h2>
<%= simple_form_for @member do |f| %>

    # user fields
    <%= f.fields_for :user do |u| %>
      <%= u.input :email, :required => true, :autofocus => true %>
      <%= u.input :password, :required => true %>
      <%= u.input :password_confirmation, :required => true %>
    <% end %>

    # member fields
    <%= f.input :name %>
    <%= f.input :tel %>
    <%= f.input :city %>

    <%= f.button :submit, "Sign up" %>
<% end %>

Nested_form gemに到達する必要はないことを指摘したいと思います。要件は、ユーザーが属することができるロールは1つのタイプのみであるためです。

26
tw airball

それで何が問題なのでしょうか?ただ走れ Rails g devise:views [model_name]、各登録フォームをカスタマイズし、config/initializer/devise.rbただ置くconfig.scoped_views = true

6
Hauleth