2011-04-09 23 views
2

ポリモーフィックアップロード用のフォームで、未定義のメソッド` to_key 'に問題があります。 iは未定義メソッド `to_key for#<Class:0x17a6408> -rails-3

<%= form_for [@parent, Upload], :html => { :multipart => true } do |f| %> 

<%= form_for [parent, Upload], :html => { :multipart => true } do |f| %> 
に変更した場合

class UploadsController < ApplicationController 
    before_filter :find_parent 

    respond_to :html, :js 

    def index 
    @uploads = @parent.uploads.all unless @uploads.blank? 
    respond_with([@parent, @uploads]) 
    end 

    def new 
    @upload = @parent.uploads.new unless @uploads.blank? 
    end 

    def show 
    @upload = @parent.upload.find(params[:upload_id]) 
    end 

    def create 
    # Associate the correct MIME type for the file since Flash will change it 
    if params[:Filedata] 
     @upload.document = params[:Filedata] 
     @upload.content_type = MIME::Types.type_for(@upload.original_filename).to_s 
     @upload = @parent.uploads.build(params[:upload]) 
     if @upload.save 
     flash[:notice] = "suceessfully saved upload" 
     redirect_to [@parent, :uploads] 
     else 
     render :action => 'new' 
     end 
    end 
    end 

    def edit 
    @upload = Upload.where(params[:id]) 
    end 
    private 


    def find_parent 
    classes ||= [] 
    params.each do |name ,value| 
     if name =~ /(.*?)_id/ 
     @parent = classes << $1.pluralize.classify.constantize.find(value) 
     end 
    end 
    return unless classes.blank? 
    end 
end 

:これはコントローラで

<%= form_for [@parent, Upload], :html => { :multipart => true } do |f| %> 

    <div class="field"> 
    <%= f.label :document %><br /> 
    <%= f.file_field :document %> 
    </div> 

    <div class="actions"> 
    <%= f.submit "Upload"%> 
    </div> 
<% end %> 

これは、部分的な形であります

は、私は新しいエラーを取得する:未定義のローカル変数やメソッド `親を」#<#のために:

ActionView::Template::Error (undefined method `to_key' for #<Class:0x2205e88>): 
1: <%= render :partial => "uploads/uploadify" %> 
2: 
3: <%= form_for [@parent, Upload], :html => { :multipart => true } do |f| %> 
4: 
5: 
6: <div class="field"> 

部分が、この中にある "アップロード/ uploadify":0x21a30e0>

これはエラートレースであります要点:https://gist.github.com/911635

すべてのポインタが役に立ちます。おかげ

答えて

7

を変更する必要があります私は見ることができる、あなたのform_forは、

の行に沿って何かする必要があります
<%= form_for [@parent, @upload], :html => { :multipart => true } do |f| %> 

私はあなたのアップロードオブジェクトは、次のように別のオブジェクト内にネストされていると仮定しているよう:

resources :posts do 
    resources :uploads 
end 

渡されたときのform_forは、このような配列は、クラスに基づいて、関連するパスを作成されない何指定されたオブジェクトの新しいレコードであるかどうかを判断します。

あなたのケースでは、コントローラの新しいアクションで新しいアップロードオブジェクトを作成するので、form_forが配列を検査し、@parentのクラスとidを取得し、@uploadのクラスとIDを取得します。しかし、@uploadにはIDがないので、parent_class/parent_id/upload/upload_idにPUTするのではなく、/parent_class/parent_id/uploadにPOSTします。

問題が解決しない場合は、私に教えてください、私たちはさらにそれを把握するよ:)

- EDITを - コメントの後に -

これは@parentまたは@uploadの一つであることを意味しますなし。確認するには、次のように表示することができます。

<%= debug @parent %> 

と同じです。

# UploadsController#new 
@upload = @parent.uploads.new unless @uploads.blank? 

特にunless @uploads.blank?一部:しかし、私は@uploadがあなたのコントローラであるため、この行で、nilであることを推測しています。 ApplicationControllerで初期化しない限り、@ uploadsは常にnilです。つまり、@ uploads.blank?これは常に@uploadが決して初期化されないことを意味します。読み込む行を変更してください。

@upload = @parent.uploads.new 

この問題はうまく解決されません。 unless @uploads.blank?を使用した他の方法についても同様です。半関連のノートで

は、変数がfind_parentメソッドに対してローカルであるため、UploadsController番号のfind_parentに、あなたはこのライン

classes ||= [] 

を持って、あなたはそれが初期化されていないことを保証し、むしろすべきことができますクラスを書く= []。

はまた、あなたは右のメソッドの終了前にコード

return unless classes.blank? 

のこのラインを持っています。一度@parentが初期化されたらメソッドから戻るように追加しましたか?もしそうなら、その行は各ブロックの内側になければなりません。

さらに、クラスがメソッドの外部で使用されないため、なぜそれを定義するのですか?コードは次のように読んで、まだ他のものの中で同じ動作

def find_parent 
    params.each do |name ,value| 
    @parent = $1.pluralize.classify.constantize.find(value) if name =~ /(.*?)_id/ 
    return if @parent 
    end 
end 

を持つことができ、あなたは、これはいくつかのことをしていることがわかります:

  1. が必要とされていない変数の初期化を避けることができます。
  2. 単一行条件文の可読性に役立つif文のインライン化
  3. unless variable.blankif variableの使用の変更点を示します。あなたの変数がブール値でない限り、これは同じことを成し遂げますが、前者は本質的にあなたの脳が解析しなければならない二重否定であるため、認知負荷を軽減します。

- EDIT - 問題についてのメール交換から -

あなたは正しいです - 親が初期化されている場合if @parentはtrueを返します。しかし、私がSOに言及したように、例外は@parentが初期化され、falseに設定されている場合です。基本的には、Rubyでは、nilとfalseを除くすべての値が真とみなされます。インスタンス変数が初期化されていない場合、デフォルト値はnilであるため、そのコード行が機能します。それは理にかなっていますか?

In terms of setting @parent in each action that renders form in the UsersController, which of these is the correct way to do this on the index action. I have tried all 3 but got errors

@parentと@uploadの両方がActiveRecord(AR)オブジェクトのインスタンスである必要があります。最初のケースでは、@parentをUser.allに設定します。これは機能しないARオブジェクトの配列です。また、@ parentを初期化する前に@ parent.uploadsを呼び出そうとすると、メソッドエラーが発生しません。しかし、2行をスワップする場合でも、parentが配列の場合は@ parent.uploadsを呼び出すことになります。アップロードメソッドは個々のARオブジェクトで定義されており、それらの配列では定義されていないことに注意してください。インデックスの3つの実装はすべて同様のことをするので、上記の警告はさまざまな形ですべてのものに当てはまります。

users_controller.rb

def index @upload = @parent.uploads @parent = @user = User.all end

or 

def index # @user = @parent.user.all @parent = @user = User.all end

or 

def index @parent = @upload = @parent.uploads @users = User.all
end

私が行った変更をすばやくご案内します。私が開始する前に、私はこの

<%= render "partial_name", :variable1 => a_variable, :variable2 => another_variable %> 

この

<%= render :partial => "partial_name", :locals => {:variable1 => a_variable, :variable2 => another_variable} %> 

を行うことに相当し、レンダリングのちょうど短い(とややクリーナー)の方法であることを説明しなければなりません。同様に、コントローラでは、あなたはあなたがコードに今すぐhttp://guides.rubyonrails.org/layouts_and_rendering.htmlでこれについての詳細を読むことができる代わりに、

render :action => "new" 

render "new" 

を行うことができます。

#app/views/users/_form.html.erb 
<%= render :partial => "uploads/uploadify" %> 

<%= form_for [parent, upload], :html => { :multipart => true } do |f| %> 


<div class="field"> 
    <%= f.label :document %><br /> 
    <%= f.file_field :document %> 
    </div> 

    <div class="actions"> 
    <%= f.submit "Upload"%> 
    </div> 
<%end%> 

アップロードフォームで、@ parentと@uploadを親とアップロードに変更したことがわかります。これは、フォームの代わりにフォームをレンダリングするときに、コントローラが設定したインスタンス変数を探すために変数を渡す必要があることを意味します。

#app/views/users/index.html.erb 
<h1>Users</h1> 
<table> 
    <% @users.each do |user| %> 
    <tr> 
     <td><%= link_to user.email %></td> 
     <td><%= render 'uploads/form', :parent => user, :upload => user.uploads.new %></td> 
    </tr> 
    <% end %> 
</table> 

UsersController#indexに各ユーザーのアップロードフォームを追加します。 parentとuploadで明示的に渡すので、同じページに複数のアップロードフォームを置くことができます。それが設定されているか、親とアップロードすぐに明らかになり、これは、パーシャルを埋め込むにずっとクリーンでより拡張的なアプローチです。インスタンス変数のアプローチでは、コードベースに慣れていない人が@parentと@uploadが設定されている場所を決定するために苦労するかもしれない、など

#app/views/users/show.html.erb 
<div> 
    <% @user.email %> 
    <h3 id="photos_count"><%= pluralize(@user.uploads.size, "Photo")%></h3> 
    <div id="uploads"> 
    <%= image_tag @user.upload.document.url(:small)%> 
    <em>on <%= @user.upload.created_at.strftime('%b %d, %Y at %H:%M') %></em> 
    </div> 

    <h3>Upload a Photo</h3> 
    <%= render "upload/form", :parent => @user, :upload => user.uploads.new %> 
</div> 

私たちは親に渡すここでは、上記の変更と同様のものであり、オブジェクトをアップロードします。

#config/routes.rb 
Uploader::Application.routes.draw do 
    resources :users do 
    resources :uploads 
    end 

    devise_for :users 

    resources :posts do 
    resources :uploads 
    end 

    root :to => 'users#index' 
end 

あなたは、ルート内のトップレベルのリソースとしてアップロードを削除したことがわかります。これは、アップロードには何らかの種類の親が必要なため、トップレベルにはできないからです。

#app/views/uploads/new.html.erb 
<%= render 'form', :parent => @parent, :upload => @upload %> 

上記と同じ変更を加え、親とアップロードを明示的に渡しました。あなたはフォームをレンダリングするときはいつでもこれを行う必要があります。

#app/controllers/users_controller.rb 
class UsersController < ApplicationController 
respond_to :html, :js 

    def index 
    @users = User.all 
    end 

    def show 
    @user = User.find(params[:id]) 
    end 

    def new 
    @user = User.new 
    end 

    def create 
    @user = User.new(params[:user]) 
    if @user.save 
     redirect_to users_path 
    else 
     render :action => 'new' 
    end 
    end 

    def update 
    @user = User.find_by_id(params[:id]) 
    @user.update_attributes(params[:user]) 
    respond_with(@user) 
    end 

    def destroy 
    @user = User.find_by_id(params[:id]) 
    @user.destroy 
    respond_with(@user) 
    end 
end 

私は明示的に渡すので、ユーザーコントローラから@parentの言及を削除しました。

うまくいけば、すべてが理にかなっていること。これらの例から外挿して、アップロードフォームをレンダリングする場合はいつでも、親オブジェクトとアップロードオブジェクトを渡すことができます。あなたの提案をありがとう@floor

+0

あなたが正しいように見えます。 'upload'はメソッドではありませんが、オブジェクトです。 – fl00r

+0

@lukeあなたの時間と指導に感謝します。ルート上では、アップロードオブジェクトは別のオブジェクトにネストされています。変更を実装しましたが、NilClassのための未定義のメソッド 'model_name 'を返します:クラス**。エラーメッセージの簡単な抜粋です:**抽出されたソース(行#3の周り):**。これは3行目です。<%= form_for [@parent、@upload]、:html => {:multipart => true} do | f | %> **ありがとう。 – brg

+0

@brg私は上記の私の答えを更新しました。 –

0

[@parent、アップロード] => [@parent、:アップロード]

<%= form_for [@parent, :upload], :html => { :multipart => true } do |f| %> 

UPD

あなたは場所に:upload、何から@parent

<%= form_for [:upload, @parent], :html => { :multipart => true } do |f| %> 
+0

。私は、その変更を実装しているが、それは今、私は、この新しいエラーを与え、それは私が以前に掲載されたものから作られた唯一の変化であった:シンボルのための**未定義のメソッド 'MODEL_NAME」:クラス** – brg

+0

OK、私の更新を参照してください。 – fl00r

+0

@flOOrありがとうございました。私は彼らの場所を交換することによって新しいアップデートを実装しましたが、エラーはまだ返されました。これは、NilClassのための**未定義のメソッド 'model_name 'です:クラス**以前は** Symbol:Class **でした。おかげで再び – brg

関連する問題