2014-02-24 28 views
1

我使用設計進行註冊,使用Rails 4和Ruby 2 我已經定義了一個用戶模型,該模型與演講者或組織(我尚未編程此模型)具有多態關聯。我創建了自己的Devise RegistrationController,用於在初始化用戶對象時初始化適當的對象(揚聲器)。根據一條特殊路線。我已經爲Speaker對象編輯了新的註冊表單。 我想要實現的是當用戶填寫所有字段(來自揚聲器的用戶和字段的字段)時,創建適當的對象。因此,具有指向Speaker對象的鏈接的User對象。但是,當我提交表單我有以下錯誤,而是我得到了下面的錯誤,我目前的執行情況:如何用多態設計用戶模型解決未定義的方法`model_name'?

undefined method `model_name' for NilClass:Class 

提取的源(左右線#35):

<%= 
fields_for resource.identifiable do | identifiable_fields | 
render "#{resource.identifiable.class.name.downcase}_fields", f: identifiable_fields 
end 
%> 

該型號如下:

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 :identifiable, :polymorphic => true 
end 
class Speaker < ActiveRecord::Base 

    has_one :user, as: :identifiable 

    validates :first_name, :presence => true, length: { in: 2..20 } 
    validates :last_name, :presence => true, length: { in: 2..20 } 

    def full_name 
     [first_name, last_name].join(' ') 
    end 

    def full_name_reverse 
     [last_name, first_name].join(', ') 
    end 
end 

UserRegistrati onsController:

class UserRegistrationsController < Devise::RegistrationsController 

    # Devise RegistrationsController override. 
    # version 3.2.3 

    def new 
     head :not_implemented and return unless is_identifiable_name?(params[:identifiable_name]) 
     build_resource_with_identity({},params[:identifiable_name]) 
     respond_with self.resource 
    end 

    def create 
     build_resource(sign_up_params) 
     puts sign_up_params 
     if resource.save 
      yield resource if block_given? 
      if resource.active_for_authentication? 
       set_flash_message :notice, :signed_up if is_flashing_format? 
       sign_up(resource_name, resource) 
       respond_with resource, :location => after_sign_up_path_for(resource) 
      else 
       set_flash_message :notice, :"signed_up_but_#{resource.inactive_message}" if is_flashing_format? 
       expire_data_after_sign_in! 
       respond_with resource, :location => after_inactive_sign_up_path_for(resource) 
      end 
     else 
      clean_up_passwords resource 
      respond_with resource 
     end 
    end 


    private 

    def build_resource_with_identity(hash = nil, identifiable_name) 
     self.resource = resource_class.new_with_session(hash || {}, session) 
     self.resource.identifiable = identifiable_name.downcase.camelize.constantize.new 
    end 

    def is_identifiable_name?(identifiable_name) 
     ['speaker'].include? identifiable_name.downcase 
    end 

end 

路線爲UserRegistrationsController#新

get 'speakers/register', to: 'user_registrations#new', defaults: { identifiable_name: 'speaker' }, as: :new_speaker_registration 

新用戶註冊形式:

<h2>Registreren</h2> 

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

    <%= devise_error_messages! %> 

    <div class="row"> 
     <div class="small-3 columns"> 
      <%= f.label :email, 'Email', class: "right inline" %> 
     </div> 
     <div class="small-9 columns"> 
      <%= f.email_field :email, :autofocus => true %> 
     </div> 
    </div> 

    <div class="row"> 
     <div class="small-3 columns"> 
      <%= f.label :password, 'Wachtwoord', class: "right inline" %> 
     </div> 
     <div class="small-9 columns"> 
      <%= f.password_field :password, :autofocus => true %> 
     </div> 
    </div> 

    <div class="row"> 
     <div class="small-3 columns"> 
      <%= f.label :password_confirmation, 'Bevestig wachtwoord', class: "right inline" %> 
     </div> 
     <div class="small-9 columns"> 
      <%= f.password_field :password_confirmation, :autofocus => true %> 
     </div> 
    </div> 

    <%= 
     fields_for resource.identifiable do | identifiable_fields | 
      render "#{resource.identifiable.class.name.downcase}_fields", f: identifiable_fields 
     end 
    %> 

    <div class="row"> 
     <div class="small-9 small-offset-3"> 
      <%= f.submit "Registreren", class: "small button radius" %> 
     </div> 
    </div> 

<% end %> 

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

揚聲器字段部分:

 <div class="row"> 
    <div class="small-3 columns"> 
     <%= f.label :first_name, 'Voornaam', class: "right inline" %> 
    </div> 
    <div class="small-9 columns"> 
     <%= f.text_field :first_name, :autofocus => true %> 
    </div> 
</div> 

<div class="row"> 
    <div class="small-3 columns"> 
     <%= f.label :last_name, 'Achternaam', class: "right inline" %> 
    </div> 
    <div class="small-9 columns"> 
     <%= f.text_field :last_name, :autofocus => true %> 
    </div> 
</div> 

日誌:

應用跟蹤:

app/views/user_registrations/new.html.erb:35:in `block in _app_views_user_registrations_new_html_erb___3888261175776937705_70060949603300' 
app/views/user_registrations/new.html.erb:3:in `_app_views_user_registrations_new_html_erb___3888261175776937705_70060949603300' 
app/controllers/user_registrations_controller.rb:28:in `create' 

參數發送:

{"utf8"=>"✓", 
"authenticity_token"=>"vDLeKE+CJvzZa/GDcE9KqvV8jszWhc5uPBvBgBkTjO0=", 
"user"=>{"email"=>"", 
"password"=>"[FILTERED]", 
"password_confirmation"=>"[FILTERED]"}, 
"speaker"=>{"first_name"=>"", 
"last_name"=>""}, 
"commit"=>"Registreren"} 
+0

'resource.identifiable'在你的視圖中是零 - 在哪裏設置? – sevenseacat

+0

@sevenseacat在UserRegistrationsController的'build_resource_with_identity'方法中。這是行:'self.resource.identifiable = identifiable_name.downcase.camelize.constantize.new' – Nazeem

+0

是在呈現表單或提交表單時出現錯誤?我們可以看到一些日誌嗎? – sevenseacat

回答

0

我已經能夠解決未定義的方法的問題:

undefined method `model_name' for NilClass:Class 

正如成員sevenseacat提到,當提交表格時,resource.identifiablenil創建方法UserRegistrationsController。解決方法是確保resource.identifiablecreate方法中設置了適當的對象,因爲我正在使用用戶模型中的多態關聯。因此,例如,如果我要註冊一個揚聲器User.identifiable(用戶是設計型號)必須返回一個揚聲器對象。


請注意,編寫解決方案的時候,我已經重寫/優化的一些代碼進行比較,我張貼在我的答案代碼。


首先,我改變了我的的routes.rb。 我已經給出了它應該與默認散列中的用戶模型關聯的模型的名稱。

get 'speakers/register', to: 'user_registrations#new', defaults: { identifiable_type: 'Speaker' }, as: :new_speaker_registration 

get 'organizations/register', to: 'user_registrations#new', defaults: { identifiable_type: 'Organization' }, as: :new_organization_registration 

user_registrations_controller.rb我改變了一些事情。

新方法new方法,我檢查,如果自帶的默認設置散列中的參數,有兩種價值揚聲器組織,以確保是唯一允許的值。 後通常用戶目標是建立,我通過調用constantize然後new建立適當的身份(揚聲器組織)對象。

創建方法 在create方法中,devise將用其參數構建User對象。 然後,我正在提取需要與用戶對象關聯的對象的名稱。對象的名稱通過表單傳入。然後這個名字是一致的,所以可以調用new

in Rails 4默認情況下不允許批量分配。您必須指定允許哪些屬性。使用方法identifiable_resource_params,我將根據對象的名稱檢查要傳入的參數。


class UserRegistrationsController < Devise::RegistrationsController 

    # Devise RegistrationsController override. 
    # version 3.2.3 

    def new 
     head :not_implemented and return unless is_identifiable_type?(params[:identifiable_type]) 
     build_resource 
     self.resource.identifiable = params[:identifiable_type].constantize.new  
     respond_with self.resource 
     end 

    def create 
     build_resource(sign_up_params) 
     identifiable_type = params[:user][:identifiable_type].to_s 
     resource.identifiable = identifiable_type.constantize.new(identifiable_resource_params(identifiable_type)) 
     if resource.save 
      yield resource if block_given? 
      if resource.active_for_authentication? 
       set_flash_message :notice, :signed_up if is_flashing_format? 
       sign_up(resource_name, resource) 
       respond_with resource, :location => after_sign_up_path_for(resource) 
      else 
       set_flash_message :notice, :"signed_up_but_#{resource.inactive_message}" if is_flashing_format? 
       expire_data_after_sign_in! 
       respond_with resource, :location => after_inactive_sign_up_path_for(resource) 
      end 
     else 
      clean_up_passwords resource 
      respond_with resource 
     end 
    end 

    private 

    def is_identifiable_type? identifiable_type 
     ['Speaker','Organization'].include? identifiable_type 
    end 

    def is_speaker_type? identifiable_type 
     'Speaker'.eql?(identifiable_type) 
    end 

    def is_organization_type? identifiable_type 
     'Organization'.eql?(identifiable_type) 
    end 

    def identifiable_resource_params identifiable_type 
     if is_speaker_type? identifiable_type 
      params.require(identifiable_type.underscore.to_sym).permit(:first_name, :last_name) 
     elsif is_organization_type? identifiable_type 
      params.require(identifiable_type.underscore.to_sym).permit(:name, :description, :category, :contact_first_name, :contact_last_name, :contact_email) 
     end 
    end 

end 

new.html.erb我加了兩件事情。 首先是一些代碼,用於渲染包含特定字段的適當部分,用於揚聲器組織對象。其次,我確定需要與對象關聯的對象的名稱通過,因此控制器中的create方法具有關於它的知識。

相關問題