Entwickeln und mehrere "Benutzermodelle"

73

Ich benutze Rails 3.2 und entwickle 2.0 und bin ziemlich neu in Rails.

Bedarf

Ich möchte Folgendes erreichen:

  • 2 oder mehr "Benutzer" -Modelle haben, z. Mitglied, Kunde, Admin
  • Alle Modelle haben einige erforderliche Felder gemeinsam (z. B. E-Mail und Passwort).
  • Jedes Modell kann einige eindeutige Felder haben (z. B. Firma nur für Kunden).
  • Einige Felder können gemeinsam genutzt werden, haben jedoch nicht dieselbe Validierung (z. B. Name ist für den Kunden erforderlich, für Mitglied jedoch optional).
  • Alle Felder müssen während des Registrierungsprozesses ausgefüllt werden, daher sind die Formulare unterschiedlich
  • Das Anmeldeformular sollte eindeutig sein

Mögliche Lösungen

Ich habe ziemlich lange gegoogelt und StackOverflow durchsucht, aber mir scheint nichts richtig zu sein (ich bin ein Java-Typ, sorry :) und jetzt bin ich ziemlich verwirrt. Es wurden zwei Lösungen gefunden:

Einzelner Benutzer

Das ist die häufigste Antwort. Erstellen Sie einfach den Standardbenutzer und erstellen Sie Beziehungen zwischen Mitglied -> Benutzer und Kunde -> Benutzer. Mein Anliegen ist hier, wie ich einen individuellen Registrierungsprozess für jedes Modell erreichen kann. Ich habe verschiedene Dinge ausprobiert, aber alles endete als Chaos!

Benutzer mit mehreren Geräten

Dies löst den benutzerdefinierten Registrierungsprozess und scheint mir richtig zu sein, aber das eindeutige Anmeldeformular ist ein Blocker. Ich habe auf SO eine Antwort gefunden ( Devise - Login von zwei Modellen ), die vorschlägt, Devise :: Models :: Authenticatable.find_for_authentication (Bedingungen) zu überschreiben. Das scheint kompliziert (?) Und da ich neu in Schienen bin, würde ich gerne wissen, ob das funktionieren könnte?

Danke für deinen Rat!

ddidier
quelle

Antworten:

69

Willkommen an Bord von Java guy =), ich hoffe du wirst die Rails Welt genießen. Um Ihr Problem zu lösen, haben Sie einfach zwei Lösungen:

  1. Erstellen Sie für jeden Benutzer eine Tabelle in der Datenbank und das entsprechende Modell.
  2. Erstellen Sie eine einzelne Tabelle in der Datenbank und erstellen Sie für jeden Benutzertyp ein Modell. Dies wird als Single Table Vererbung (STI) bezeichnet.

Welches soll ich wählen? Dies hängt von den gemeinsamen Attributen der Rollen ab. Wenn sie fast häufig vorkommen (zum Beispiel haben alle einen Namen, eine E-Mail-Adresse, ein Mobiltelefon usw.) und einige Attribute unterschiedlich sind, empfehle ich die STI-Lösung.

Wie mache ich die STI? 1. Erstellen Sie einfach das Gerätemodell und die Tabelle mit dem Befehl. rails generate devise User 2. Fügen Sie typeder Benutzertabelle in der Datenbank mithilfe einer Migration eine Spalte mit dem Datentyp string hinzu. 3. Erstellen Sie für jeden Benutzertyp ein Modell (z. B. rails g model admin). 4. Lassen Sie die Admin-Klasse vom Benutzermodell erben

class Admin < User
end

Das war's, du bist fertig =) ... Yupeee

Um einen Administrator zu erstellen, führen Sie den Befehl aus, Admin.create(...)bei dem die Punkte die Administratorattribute sind, z. B. E-Mail, Name, ...

Ich denke, diese Frage könnte Ihnen auch helfen

mohamagdy
quelle
1
Ich tue es, danke :) Ich war bis jetzt für einen Neuling ziemlich erfolgreich ... in der Tat haben Kunden viele Attribute und Mitglieder nur wenige. Ist Ihre Lösung eine "Einzelbenutzer" oder eine "Mehrfachbenutzer", ist mir das nicht klar. Wenn das später ist, brauche ich config.scoped_views = true?
Ddidier
Haben sie viele Attribute gemeinsam? Wenn ja, dann empfehle ich die STI-Lösung. Wenn nein, erstellen Sie für jeden Benutzertyp (in Ihrem Fall Kunde und Mitglied) eine Tabelle in der Datenbank und ein entsprechendes Modell. Auf jeden Fall können Sie die Lösung wählen, die zu Ihrem Problem passt.
Mohamagdy
Soweit ich von Ihnen verstanden habe, ist es die Einzelbenutzer-Tabelle (Einzelbenutzertabelle in der Datenbank, in der alle Benutzer, Mitglieder und Kunden
gespeichert sind
2
Vielen Dank! Randnotiz: Ich denke, Rails G Model erstellt auch die Migrationsdateien für dieses Modell. In diesem Fall erstellen wir keine neue Tabelle für den Administrator. Ich denke, Sie müssen lediglich eine Datei mit dem Namen admin.rb im Modellordner erstellen, anstatt den Rails-Generator zu verwenden.
strohy1210
27

Ich bin in ähnlichen Schuhen wie Sie, nachdem ich alle möglichen Ansätze ausprobiert hatte, entschied ich mich für ein einzelnes Benutzermodell, das zu polymorphen Rollen gehören würde. Dies scheint der einfachste Weg zu sein, um eine einmalige Anmeldung zu erreichen.

Das Benutzermodell würde nur die Informationen enthalten, die für die Anmeldung spezifisch sind.

Das Rollenmodell würde für jede Rolle spezifische Felder sowie andere für die Rolle spezifische Zuordnungen speichern.

Neue Registrierungen würden für jeden Benutzertyp (Rollen) über einzelne Controller angepasst und dann verschachtelte Attribute für den Benutzer erstellt.

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 

Routen - nur normales Zeug für das Mitgliedsmodell.

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

Controller - Sie müssen build_user für verschachtelte Attribute erstellen

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

def create
    #... standard controller stuff
end

Ansichten / Mitglieder / 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 %>

Ich möchte darauf hinweisen, dass es KEINE NOTWENDIGKEIT gibt, nach nested_form gem zu greifen. da die Anforderung ist, dass der Benutzer nur zu einem Rollentyp gehören kann.

tw Airball
quelle
Ich habe diesen ähnlichen Ansatz in meinem Projekt verwendet, aber nach der Anmeldung wird die Benutzersitzung nicht beibehalten, sodass immer die Anmeldeseite angezeigt wird, die nicht angezeigt werden sollte. Hast du eine Idee davon?
Ahmad Hamza
1
Aus Datenbanksicht ist es besser, sie has_oneim Benutzermodell und belongs_toim Mitgliedsmodell zu haben, damit Sie die polymorphe Beziehung vermeiden können . Gibt es einen Grund, warum Sie das Gegenteil getan haben?
Collimarco
@collimarco legitime Frage, gilt aber polymorphicnur für belongs_to. Es gibt andere Lösungen in Bezug auf "revers polymorph", die jedoch nicht in den Geltungsbereich fallen.
Tw Airball
18

Ich habe einen Weg gefunden und bin bisher ziemlich zufrieden damit. Ich werde es hier für andere beschreiben.

Ich ging mit der einzelnen "Benutzer" -Klasse. Mein Problem war es, für jedes Pseudomodell einen individuellen Registrierungsprozess zu erreichen.

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

Dann habe ich http://railscasts.com/episodes/217-multistep-forms und http://pastie.org/1084054 so angepasst , dass zwei Registrierungspfade mit einem überschriebenen Controller vorhanden sind:

config / route.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'

controller / users / registrations_controller.rb:

Ich habe eine Assistentenklasse erstellt, die die Felder kennt, die bei jedem Schritt überprüft werden müssen

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

und meine Ansichten sind:

  • new.rb
  • new_client.rb einschließlich eines Teils gemäß dem Assistentenschritt:
    • _new_client_1.rb
    • _new_client_2.rb
  • new_member.rb einschließlich eines Teils gemäß dem Assistentenschritt:
    • _new_member_1.rb
    • _new_member_2.rb
ddidier
quelle
6

Also, was ist falsch? Einfach ausführen rails g devise:views [model_name], jedes Registrierungsformular anpassen und config/initializer/devise.rbeinfach eingeben config.scoped_views = true.

Hauleth
quelle
1
Danke, aber AFAIK, die das einzelne Anmeldeformular nicht löst, oder?
Ddidier
1
Entschuldigung, ich habe gerade gelesen: "Danach können Sie Ansichten basierend auf der Rolle" Benutzer / Sitzungen / Neu "und" Administratoren / Sitzungen / Neu "haben. Wenn innerhalb des Bereichs keine Ansicht gefunden wird, wird Devise verwendet Die Standardansicht unter "devise / session / new". Sie können den Generator auch zum Generieren von Ansichten mit Gültigkeitsbereich verwenden: "damit das funktioniert ...
ddidier
Ich habe versucht, diesen Weg zu gehen. Ich habe jetzt Routen für Mitglieder und Kunden, einige Ansichten werden geteilt, andere nicht. Das Problem mit dem einzelnen Anmeldeformular bleibt jedoch bestehen ... Wie kann ein eindeutiges Formular zur Authentifizierung eines Mitglieds ODER eines Kunden verwendet werden?
Ddidier
Haben Sie die scoped_viewsOption aktiviert config/initializers/devise.rb? Es ist standardmäßig deaktiviert, um die Leistung zu verbessern.
Hauleth
@c_inconnu fand diesen Link gist.github.com/jeremyw/5319386 , um ein Anmeldeformular für mehrere Modelle zu teilen
Jude Calimbas