從什麼我可以看到,你的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,它將POST /parent_class/parent_id/upload
而不是PUTting parent_class/parent_id/upload/upload_id
。
讓我知道,如果不工作,我們將進一步弄清楚:)
- 編輯 - 意見後 -
這意味着@parent或@upload之一是零。要檢查,你可以在你的視圖中輸入以下內容:
<%= debug @parent %>
和@upload一樣,看哪個是零。不過,我猜@upload是零,因爲這條線在你的控制器:
# UploadsController#new
@upload = @parent.uploads.new unless @uploads.blank?
特別是unless @uploads.blank?
部分。除非你在ApplicationController中初始化它,@uploads總是零,這意味着@ uploads.blank?將永遠是真實的,這又意味着@upload永遠不會被初始化。改行
@upload = @parent.uploads.new
問題將有望得到解決。其他使用unless @uploads.blank?
的方法也是如此。
在一個半相關的說明,在UploadsController#find_parent,你有這條線
classes ||= []
,因爲變量是本地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
除其他事項外,你會看到這個做了幾件事情:
- 避免了初始化是不需要的變量。
- 內聯if語句,它有助於單線條件的可讀性
- 將
unless variable.blank
更改爲if variable
的使用。除非你的變量是一個布爾值,否則這會完成同樣的事情,但是會減少認知負擔,因爲前者基本上是你的大腦必須分析的雙重否定。
- 編輯 - 從有關問題的電子郵件交流 -
你是正確的 - 如果父母被初始化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,這會導致無方法錯誤。但是,即使您要交換兩行,當父數組是數組時,也會調用@ parent.uploads。請記住,uploads方法是在單個AR對象上定義的,而不是在它們的數組上定義的。由於你的所有三個索引實現都做類似的事情,所以上述注意事項以各種形式適用於它們。
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} %>
,並渲染只是一個較短的(而且有些清潔劑)的方式。同樣,在一個控制器,你可以做
render "new"
,而不是
render :action => "new"
您可以在http://guides.rubyonrails.org/layouts_and_rendering.html現在到代碼閱讀更多關於這一點。
#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被設置等
#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,因爲我們通過顯式傳遞它。
希望大家都有道理。您可以從這些示例中推斷,並通過父級和上傳對象,無論您想要呈現上傳表單。
看起來你是對的。 'upload'不是方法,而是對象:) – fl00r 2011-04-10 14:45:49
@luke感謝您的時間和指導。是的,在路由中,上傳對象嵌套在另一個對象中。我已經實現了這個改變,但是它現在返回**未定義的方法'model_name'用於NilClass:Class **。以下是錯誤消息的簡要摘錄:**提取的源代碼(第3行左右):**。這是第3行** <%= form_for [@parent,@upload],:html => {:multipart => true} do | f | %> **。謝謝。 – brg 2011-04-10 15:18:18
@brg我已經更新了上面的答案。 – 2011-04-10 15:42:56