我是Ruby和Rails的新手,所以对我想做的事情可能有更好的方法,但我很感激任何帮助,理解为什么我的方法确实失败而不是不同的方法。我正在使用:
我有一个Skin.rb模型(外观上的皮肤,而不是风琴),我有一个适用于Android环境的皮肤和适用于iOS环境的不同皮肤。皮肤可以有零个或一个与之关联的语言文件以及与之关联的零个或一个图形文件。这些皮肤的属性显示在app \ views \ skins \ 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_file
和graphics_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
的工作原理(简单化)如下:
skins.id
= 4的记录,因此创建了一个新记录skins.id
= 4的记录,以便更新记录,其属性按POST请求中的属性设置。http://apidock.com/rails/ActiveRecord/Base/save沙rails activerecord save method都暗示我做的是正确的,但它不起作用。
我观察到的是,每次尝试配置其中一个现有皮肤时,都会在皮肤表中创建一个新的皮肤记录,其中skins.id
会自创建的最后一个皮肤自动递增。 params[:skin][:id]
似乎被忽略了。
我可以使用new
和save
方法根据需要更新/创建新记录吗?我怎么做?我想我已经将足够的信息传递给了我的SkinsController,所以我希望答案存在于SkinsController#create
方法本身。
(至于为什么我这样做的时候可能有更好的方法:
http://.../skins
和update_attributes
。此外,我认为他们无论如何,只需环绕save
。)我想了解我的代码是如何失败的,而不是其他方法可能更好。
答案 0 :(得分:1)
使用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