Pergunta

Sou novo em Ruby e Rails, então provavelmente há uma abordagem melhor para o que quero fazer, mas agradeceria qualquer ajuda para entender por que exatamente minha abordagem falha, em vez de como seria uma abordagem diferente.Estou a usar:

  • Rubi 1.8.7
  • Trilhos 3.2.12
  • Redmine 2.2.3 (embora eu não ache que seja totalmente relevante aqui)
  • MySQL 5.6

Eu tenho um modelo Skin.rb (pele como aparência, não órgão) e tenho um skin para ambientes Android e um skin diferente para ambientes iOS.Uma capa pode ter zero ou um arquivo de idioma associado a ela e zero ou um arquivo gráfico associado a ela.Os atributos para essas skins são exibidos na visualização app\views\skins\index.html.erb que lista cada uma das skins:

<% @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 %>

Gostaria de poder atualizar os atributos do skin do Android ou do skin do iOS na visualização do índice e atualizar o registro apropriado na tabela de skins.No entanto, quando tento atualizar o registro, um novo registro é criado em vez de o registro relevante ser atualizado.

A maneira como estou tentando fazer isso é passar o skin atualizado da visualização do índice com seu id e atualizado lang_file e graphics_pack atributos para o skins_controller#create método.O POST rastreado pelo WEBrick se parece com isto:

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"}}

Você pode ver o params[:skin] parâmetro passado acima.

Este método usa o new método para criar um novo objeto Skin com os atributos passados ​​em params[:skin].O create O método é o seguinte (os comentários referem-se ao rastreamento do WEBrick acima):

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

Tanto quanto eu entendo, desde skin.id é a chave primária para o skins mesa, save funciona (simplesmente) da seguinte forma:

  1. Atualmente não há registro com skins.id=4 então um novo é criado
  2. Já existe um registro com skins.id=4 para que o registro seja atualizado com seus atributos definidos conforme aqueles na solicitação POST.

http://apidock.com/rails/ActiveRecord/Base/save areia Método de salvamento do ActiveRecord do Rails ambos sugerem que estou fazendo a coisa certa, mas não está funcionando.

O que observo é que cada vez que tento configurar um dos skins existentes, um novo registro de skin é criado na tabela de skins com seu skins.id incrementado automaticamente a partir do último criado.O params[:skin][:id] parece ser ignorado.

Posso usar o new e save métodos para atualizar/criar um novo registro conforme necessário?Como faço isso?Acho que estou passando informações suficientes para meu SkinsController, então espero que a resposta esteja no SkinsController#create método em si.

(Quanto ao motivo pelo qual estou fazendo isso dessa maneira, quando provavelmente existem maneiras melhores:

  1. Meus casos de uso são tais que já deve haver uma capa Android e uma capa iOS no momento em que o usuário navega para http://.../skins e
  2. Eu acho que é bom atualizar/criar perfeitamente esses registros com o mesmo trecho de código, se a linguagem permitir, então estou evitando os vários métodos específicos de atualização no Rails (por exemplo, update_attributes.Além disso, acho que eles apenas envolvem save de qualquer forma.)

Gostaria de entender como meu código está falhando e não qual outra abordagem pode ser melhor.

Foi útil?

Solução

Usar 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] será ignorado porque é um atributo protegido.Você não pode atribuir em massa 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 
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top