Domanda

Sono nuovo in Ruby e Rails, quindi probabilmente c'è un approccio migliore a ciò che voglio fare, ma apprezzerei qualsiasi aiuto per capire perché esattamente il mio approccio fallisce piuttosto che come sarebbe un approccio diverso.Sto usando:

  • Rubino 1.8.7
  • Rotaie 3.2.12
  • Redmine 2.2.3 (anche se non credo sia del tutto rilevante qui)
  • MySQL 5.6

Ho un modello Skin.rb (skin come in apparenza, non organo) e ho una skin per ambienti Android e una skin diversa per ambienti iOS.A uno skin possono essere associati zero o un file di lingua e zero o un file di grafica associati.Gli attributi di questi skin vengono visualizzati nella vista app\views\skins\index.html.erb che elenca ciascuno degli skin:

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

Mi piacerebbe poter aggiornare gli attributi della skin Android o della skin iOS nella visualizzazione indice e aggiornare il record appropriato nella tabella delle skin.Tuttavia, quando provo ad aggiornare il record, viene creato un nuovo record invece dell'aggiornamento del record pertinente.

Il modo in cui sto cercando di farlo è passare la skin aggiornata dalla vista indice con il suo id e aggiornato lang_file E graphics_pack attribuisce al skins_controller#create metodo.Il POST tracciato da WEBrick si presenta così:

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

Puoi vedere il params[:skin] parametro passato sopra.

Questo metodo utilizza il new metodo per creare un nuovo oggetto Skin con gli attributi passati params[:skin].IL create il metodo si presenta come segue (i commenti si riferiscono alla traccia WEBrick sopra):

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

Per quanto ho capito, da allora skin.id è la chiave primaria per il file skins tavolo, save funziona (semplicisticamente) come segue:

  1. Al momento non esiste alcun record con skins.id=4 quindi ne viene creato uno nuovo
  2. Esiste già un record con skins.id=4 in modo che il record venga aggiornato con i suoi attributi impostati secondo quelli nella richiesta POST.

http://apidock.com/rails/ActiveRecord/Base/save sabbia metodo di salvataggio del record attivo di rails entrambi suggeriscono che sto facendo la cosa giusta ma non funziona.

Quello che osservo è che ogni volta che provo a configurare una delle skin esistenti, viene creato un nuovo record di skin nella tabella skin con il suo skins.id incrementato automaticamente dall'ultimo creato.IL params[:skin][:id] sembra essere ignorato.

Posso usare il new E save metodi per aggiornare/creare un nuovo record secondo necessità?Come lo faccio?Penso di passare abbastanza informazioni al mio SkinsController, quindi mi aspetto che la risposta si trovi nel file SkinsController#create metodo stesso.

(Per quanto riguarda il motivo per cui lo sto facendo in questo modo quando probabilmente ci sono modi migliori:

  1. I miei casi d'uso sono tali che dovrebbero già essere presenti una skin Android e una skin iOS nel momento in cui l'utente accede http://.../skins E
  2. Penso che sia bello aggiornare/creare questi record senza problemi con lo stesso bit di codice se la lingua lo consente, quindi evito i vari metodi specifici di aggiornamento in rails (ad es. update_attributes.Inoltre, penso che si avvolgano e basta save Comunque.)

Mi piacerebbe capire come sta fallendo il mio codice piuttosto che quale altro approccio potrebbe essere migliore.

È stato utile?

Soluzione

Utilizzo 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] verrà ignorato perché il suo attributo protetto.Non è possibile assegnare in 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 
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top