Rails ActiveRecord.new создает новую запись вместо обновления существующей записи
Я новичок в Ruby и Rails, так что, вероятно, есть лучший подход к тому, что я хочу сделать, но я был бы признателен за любую помощь, чтобы понять, почему именно мой подход терпит неудачу, а не как выглядит другой подход. Я использую:
- Ruby 1.8.7
- Rails 3.2.12
- Redmine 2.2.3 (хотя я не думаю, что это полностью уместно здесь)
- MySQL 5.6
У меня есть модель 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
метод. POST, отслеживаемый WEBrick, выглядит следующим образом:
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 песчаные рельсы метод activerecord save оба предлагают, что я делаю правильные вещи, но это не работает.
Я вижу, что каждый раз, когда я пытаюсь настроить один из существующих скинов, в таблице скинов создается новая запись с skins.id
автоматически увеличивается с последнего созданного. params[:skin][:id]
кажется, игнорируется.
Могу ли я использовать new
а также save
способы обновления / создания новой записи по мере необходимости? Как я могу это сделать? Я думаю, что передаю достаточно информации своему SkinsController, поэтому я ожидаю, что ответ лежит в SkinsController#create
сам метод.
(Что касается того, почему я делаю это так, когда есть, вероятно, лучшие способы:
- Мои варианты использования таковы, что к тому времени, когда пользователь переходит к пользователю, уже должен быть скин Android и скин iOS
http://.../skins
а также - Я думаю, что было бы неплохо легко обновить / создать эти записи с одним и тем же фрагментом кода, если язык позволяет это, поэтому я избегаю различных методов обновления в rails (например,
update_attributes
, Кроме того, я думаю, что они просто оборачиваютсяsave
тем не мение.)
Я хотел бы понять, как мой код терпит неудачу, а не какой другой подход может быть лучше.
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