Rails ActiveRecord.new crée un nouvel enregistrement au lieu de mettre à jour l'enregistrement existant

StackOverflow https://stackoverflow.com//questions/22020370

Question

Je suis nouveau sur Ruby et Rails, il existe donc probablement une meilleure approche de ce que je veux faire, mais j'apprécierais toute aide pour comprendre pourquoi exactement mon approche échoue plutôt que à quoi ressemblerait une approche différente.J'utilise:

  • Rubis 1.8.7
  • Rails 3.2.12
  • Redmine 2.2.3 (bien que je ne pense pas être entièrement pertinent ici)
  • MySQL 5.6

J'ai un modèle Skin.rb (peau comme en apparence, pas d'organe) et j'ai un skin pour les environnements Android et un skin différent pour les environnements iOS.Un skin peut être associé à zéro ou un fichier de langue et à zéro ou un fichier graphique associé.Les attributs de ces skins sont affichés dans la vue app\views\skins\index.html.erb qui répertorie chacun des 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 %>

J'aimerais pouvoir mettre à jour les attributs du skin Android ou du skin iOS dans la vue index et mettre à jour l'enregistrement approprié dans la table des skins.Cependant, lorsque j'essaie de mettre à jour l'enregistrement, un nouvel enregistrement est créé au lieu que l'enregistrement concerné soit mis à jour.

La façon dont j'essaie de procéder est de transmettre le skin mis à jour de la vue index avec son id et mis à jour lang_file et graphics_pack attributs au skins_controller#create méthode.Le POST tel que tracé par WEBrick ressemble à ceci :

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

Tu peux voir le params[:skin] paramètre passé ci-dessus.

Cette méthode utilise le new méthode pour créer un nouvel objet Skin avec les attributs transmis params[:skin].Le create La méthode ressemble à ceci (les commentaires font référence à la trace WEBrick ci-dessus) :

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

D'après ce que j'ai compris, puisque skin.id est la clé primaire du skins tableau, save fonctionne (de manière simpliste) comme suit :

  1. Il n'y a actuellement aucun enregistrement avec skins.id=4 donc un nouveau est créé
  2. Il existe déjà un enregistrement avec skins.id=4 afin que l'enregistrement soit mis à jour avec ses attributs définis selon ceux de la requête POST.

http://apidock.com/rails/ActiveRecord/Base/save sable méthode de sauvegarde Rails ActiveRecord les deux suggèrent que je fais la bonne chose mais cela ne fonctionne pas.

Ce que j'observe, c'est qu'à chaque fois que j'essaie de configurer un des skins existants, un nouvel enregistrement de skin est créé dans la table des skins avec son skins.id auto-incrémenté à partir du dernier créé.Le params[:skin][:id] semble être ignoré.

Puis-je utiliser le new et save méthodes pour mettre à jour/créer un nouvel enregistrement si nécessaire ?Comment je fais ça?Je pense que je transmets suffisamment d'informations à mon SkinsController, donc j'attends la réponse dans le SkinsController#create méthode elle-même.

(Quant à savoir pourquoi je le fais de cette façon alors qu'il existe probablement de meilleures façons :

  1. Mes cas d'utilisation sont tels qu'il devrait déjà y avoir un skin Android et un skin iOS au moment où l'utilisateur accède à http://.../skins et
  2. Je pense que c'est bien de mettre à jour/créer ces enregistrements de manière transparente avec le même morceau de code si le langage le permet, j'évite donc les différentes méthodes spécifiques à la mise à jour dans les rails (par ex. update_attributes.En plus, je pense qu'ils s'enroulent simplement save de toute façon.)

J'aimerais comprendre comment mon code échoue plutôt que quelle autre approche pourrait être meilleure.

Était-ce utile?

La solution

Utiliser 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] sera ignoré car son attribut protégé.Vous ne pouvez pas attribuer en masse 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 
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top