2014-02-25 177 views
2

我是Ruby和Rails的新手,所以可能有更好的方法來實現我想要做的事情,但是,如果我理解爲什麼我的方法會失敗,我將不勝感激比不同的方式看起來更像。我使用的是:Rails ActiveRecord.new創建新記錄而不是更新現有記錄

  • 的Ruby 1.8.7
  • 的Rails 3.2.12
  • 管理平臺2.2.3(雖然我不認爲是 這裏完全相關)
  • 的MySQL 5.6

我有一個Skin.rb模型(皮膚在外觀,而不是器官),我有一個皮膚的Android環境和iOS環境的不同皮膚。一個皮膚可以有零個或一個與之關聯的語言文件以及與之關聯的零個或一個圖形文件。這些外觀的屬性顯示在應用程序\意見\皮膚\ index.html.erb列出每個皮的觀點:

<% @skins.each do |skin| %> 
    <% if skin.device_os == 'android' %> 
    <%= content_tag(:h3, 'Android') %> 
    <% elsif skin.device_os == 'ios' %> 
    <%= content_tag(:h3, 'iOS') %> 
    <% end%> 

<table> 
    <thead><tr> 
     <td>Languages</td> 
     <td>Graphics</td> 
     <td></td> 
     <td></td> 
    </tr></thead> 

    <tbody>  
     <tr> 
     <%= form_for :skin, :url => skins_path do |f| %> 
      <td><%= f.collection_select :lang_file, (Attachment.find_by_sql [@lang_file_sql, @current_project.id]), :id, :filename, {:prompt => skin.lang_file.present? ? Attachment.find(skin.lang_file).filename : "Select a languages file"} %></td> 
      <td><%= f.collection_select :graphics_pack, (Attachment.find_by_sql [@graphics_pack_sql, @current_project.id]), :id, :filename, {:prompt => skin.graphics_pack.present? ? Attachment.find(skin.graphics_pack).filename : "Select a graphics pack"} %></td> 
      <td><%= hidden_field('skin', 'id', {:value => skin.id}) %></td> 
      <td><%= f.submit %></td> 
     <% end %> 
     </tr> 
    </tbody> 
    </table> 
<% end %> 

我希望能更新了Android或者屬性皮膚或索引視圖中的iOS皮膚,並在皮膚表中更新相應的記錄。但是,當我嘗試更新記錄時,將創建一條新記錄,而不是正在更新的相關記錄。

我試圖做到這一點的方法是從與其id和更新lang_filegraphics_pack屬性的skins_controller#create方法索引視圖通過更新肌膚。作爲跟蹤由使用WEBrick的POST看起來是這樣的:

Started POST "/skins" for 127.0.0.1 at Tue Feb 25 15:25:04 +0000 2014 
Processing by SkinsController#create as HTML 
    Parameters: {"authenticity_token"=>"sZWVl8IO1IKRNa/fStps8pUehDcSqQsaN/vpL3BITf8=", "commit"=>"Save 
Skin", "utf8"=>"Ô£ô", "skin"=>{"lang_file"=>"6", "graphics_pack"=>"", "id"=>"4"}} 

你可以看到params[:skin]參數上面通過。

此方法使用new方法創建一個新的Skin對象,其中的屬性在params[:skin]中傳遞。該create方法如下所示(意見提到的WEBrick上述跟蹤):

def create 
@skin = Skin.new(params[:skin]) #@skin{ id: => 4, lang_file: => 6, graphics_pack => nil } 
if @skin.save #update skins table if record with skins.id=4 already exists else create new record 
    redirect_to :back 
else 
    # do error handling stuff 
end 
end 

據我瞭解,因爲skin.id是爲skins表的主鍵,save作品(簡單地)如下:

  1. 目前與skins.id = 4那麼一個新創建
  2. 已經有一個用skins.id = 4的記錄,以便記錄與它的屬性設置爲每那些在POST REQ更新沒有記錄uest。

http://apidock.com/rails/ActiveRecord/Base/saverails activerecord save method都暗示我做的是正確的事情,但它不工作。

我觀察到的是,每次嘗試配置其中一個現有外觀時,都會在外殼表中創建一個新的外觀記錄,其中skins.id會從創建的最後一個外殼自動遞增。 params[:skin][:id]似乎被忽略。

我可以根據需要使用newsave方法更新/創建新記錄嗎?我怎麼做?我想我將足夠的信息傳遞給我的SkinsController,所以我期待着答案在SkinsController#create方法本身。

(至於爲什麼我做這種方式時,有可能是更好的方法:

  1. 我的使用情況是這樣的,應該已經由當時的用戶在Android的皮膚和在iOS皮膚導航到http://.../skins
  2. 我認爲如果語言允許它無縫更新/創建這些記錄是很好的,所以我避免了rails中的各種更新特定方法(例如update_attributes。此外,我認爲他們無論如何只需圍繞save。)

我想了解我的代碼如何失敗,而不是其他哪種方法可能更好。

+1

約定是把編輯和更新方法在你的控制器。我不認爲嘗試使用新的和創建控制器方法更新對象屬性是明智的。 –

+1

在爲資源生成路由時設置了單獨的新建,創建,編輯和更新路由的事實確實意味着我應該在一種方法中創建新皮膚,並以不同方法更新現有皮膚。但是,從用戶角度來看,只有一個按鈕可以點擊,它應該導致新的皮膚或更新的皮膚。由於有一個first_or_create方法(感謝Vimsha),所以可以將它全部放在一個方法中,我認爲它更簡潔,同時仍然保持可讀性。但也許我應該使用SkinController.update方法而不是create。 – user3337410

回答

0

使用first_or_create

def create 
@skin = Skin.where(:id => params[:skin][:id]).first_or_create #@skin{ id: => 4, lang_file: => 6, graphics_pack => nil } 
if @skin.update_attributes(params[:skin]) #update skins table if record with skins.id=4 already exists else create new record 
    redirect_to :back 
else 
    # do error handling stuff 
end 
end 

params[:skin][:id]會得到,因爲它的保護屬性被忽略。你不能批量分配id

skin = Skin.new(:id => 1, :lang_file => 6) #id will be ignored and autoincremented while saving 
skin.id = 3 #this will work. id will be set to 3 
+0

太棒了,工作!謝謝。 – user3337410

+0

請選擇正確的答案 – usha

相關問題