51 votes

Plusieurs objets dans une forme Rails

Je veux modifier plusieurs articles de mon modèle photo dans un formulaire. Je ne suis pas sûr de la façon de présenter correctement et après cela avec un formulaire, ainsi que la façon de recueillir les éléments dans la mise à jour d'action dans le contrôleur.

C'est ce que je veux:

<form>
<input name="photos[1][title]" value="Photo with id 1" />
<input name="photos[2][title]" value="Photo with id 2" />
<input name="photos[3][title]" value="Custom title" />
</form>

Les paramètres sont juste un exemple, comme je l'ai dit ci-dessus: je ne suis pas sûr de la meilleure façon de mettre ces valeurs dans le présent formulaire.

Dans le contrôleur, je veux quelque chose comme ceci:

@photos = Photo.find( params[photos] )
@photos.each do |photo|
    photo.update_attributes!(params[:photos][photo] )
end

76voto

mmell Points 644

Dans les Rails 4, juste ce

<%= form_tag photos_update_path do %>
  <% @photos.each do |photo| %> 
    <%= fields_for "photos[]", photo do |pf| %>
      <%= pf.text_field :caption %>
      ... other photo fields

48voto

austinfromboston Points 3174

Les Rails ne va pas vous aider beaucoup à faire. Il va à l'encontre de la vue standard de conventions, de sorte que vous aurez à faire des solutions de contournement dans la vue, le contrôleur, même les routes. Ce n'est pas amusant.

La clé de ressources sur la façon de traiter avec multi-modèles de formulaires, les Rails de chemin sont Stephen Chu du params-foo de la série, ou si vous êtes sur des Rails 2.3, découvrez l'Objet Imbriqué Formes

Il devient beaucoup plus facile si vous définissez une sorte de unique ressource que vous modifiez, comme une session photo. Une session photo pourrait être un réel, ActiveRecord type de modèle ou peut-être juste une façade qui accepte les données et jette les erreurs, comme si c'était un ActiveRecord modèle.

Maintenant, vous pouvez écrire un formulaire de vue un peu comme ceci:

<% form_for :photoset do |f|%>
  <% f.object.photos.each do |photo| %>
    <% f.fields_for photo do |photo_form| %>
      <%= photo_form.text_field :caption %>
      <%= photo_form.label :caption %>
      <%= photo_form.file_field :attached %>
    <% end %>
  <% end %>
<% end %>

Votre modèle doit valider chaque enfant de la Photo qui vient dans et de les agréger leurs erreurs. Vous voudrez peut-être consulter un bon article sur la façon d'inclure des Validations dans n'importe quelle classe. Il pourrait ressembler à quelque chose comme ceci:

class Photoset
  include ActiveRecord::Validations
  attr_accessor :photos

  validate :all_photos_okay

  def all_photos_okay
    photos.each do |photo|
      errors.add photo.errors unless photo.valid?
    end
  end

  def save
    photos.all?(&:save)
  end

  def photos=(incoming_data)
    incoming_data.each do |incoming|
       if incoming.respond_to? :attributes
         @photos << incoming unless @photos.include? incoming
       else
         if incoming[:id]
            target = @photos.select { |t| t.id == incoming[:id] }
         end
         if target
            target.attributes = incoming
         else
            @photos << Photo.new incoming 
         end
       end
    end
  end

  def photos
     # your photo-find logic here
    @photos || Photo.find :all
  end
end

En utilisant une façade modèle pour la session photo, vous pouvez garder votre contrôleur et vue logique, simple et directe, en se réservant le plus complexe de code pour un modèle dédié. Ce code ne sera probablement pas fonctionner hors de la boîte, mais j'espère qu'il va vous donner quelques idées et de vous diriger dans la bonne direction pour résoudre votre question.

20voto

mltsy Points 1114

Les Rails n'ont le moyen de le faire - je ne sais pas quand il a été introduit, mais en gros, c'est décrite ici: http://guides.rubyonrails.org/form_helpers.html#using-form-helpers

Il a fallu un peu de batailler pour modifier la configuration correctement pour le cas où il n'y a pas de parent de l'objet, mais ce qui semble être correct (c'est essentiellement le même que gamov de la réponse, mais plus propre et ne permet pas de "nouveaux" dossiers mélangés avec la "mise à jour" des dossiers):

<%= form_tag photos_update_path do %>
  <% @photos.each do |photo| %> 
    <%= fields_for "photos[#{photo.id}]", photo do |pf| %>
      <%= pf.text_field :caption %>
        ... [other fields]
    <% end %>
  <% end %>
<% end %>

Dans votre contrôleur, vous vous retrouverez avec un hachage en params[:photos], où les clés sont les photos d'identité et les valeurs d'attribut des hachages.

16voto

Glen Points 121

Vous pouvez utiliser le modèle "nom[]" syntaxe pour représenter plusieurs objets.

Dans la vue, de l'utilisation "de la photo[]" comme un nom de modèle.

<% form_for "photo[]", :url => photos_update_path do |f| %>
  <% for @photo in @photos %>
    <%= render :partial => "photo_form", :locals => {f => f} %>
    <%= submit_tag "Save"%>
  <% end %>
<% end %>

Cela permettra de remplir les champs de saisie comme vous l'avez décrit.

Dans votre contrôleur, vous pouvez le faire en bloc les mises à jour.

def update
  Photo.update(params[:photo].keys, params[:photo].values)
  ...
end 

Espérons que cela aidera.

Glen

8voto

gamov Points 968

En effet, comme Turadg mentionné, Rack (Rails 3.0.5) échoue si vous mélangez les nouveaux enregistrements existants dans la gorge de la réponse. Vous pouvez contourner ce problème en faisant fields_for travail manuellement:

<%= form_tag photos_update_path do %>
  <% @photos.each_with_index do |photo,i| %> 
    <%= fields_for 'photos[#{i}]', photo do |pf| %>
       <%= pf.hidden_field :id %>
        ... [other photo fields]
  <% end %>
<% end %>

C'est assez laid, si vous me demandez, mais la seule façon que j'ai trouvée pour modifier plusieurs enregistrements de mélange de nouveaux et d'enregistrements existants. Le truc c'est qu'au lieu d'avoir un tableau d'enregistrements, les paramètres de hachage obtient un tableau de hachages (numérotées i, 0,1,2, etc) ET l'id de l'enregistrement de hachage. Rails de mettre à jour les enregistrements existants en conséquence et en créer de nouveaux. Encore une remarque: Vous avez encore le processus de la nouvelle et les dossiers existants dans le contrôleur (vérifier si :id.présenter?)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X