Несколько объектов в форме Rails - PullRequest
48 голосов
/ 10 июня 2009

Я хочу редактировать несколько элементов моей модели в одной форме. Я не уверен, как правильно представить и POST это с формой, а также как собрать элементы в действии обновления в контроллере.

Вот что я хочу:

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

Параметры являются лишь примером, как я уже говорил выше: я не уверен в том, как лучше поместить эти значения в эту форму

В контроллере я хочу что-то вроде этого:

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

Ответы [ 5 ]

75 голосов
/ 27 декабря 2013

В Rails 4, только это

<%= form_tag photos_update_path do %>
  <% @photos.each do |photo| %> 
    <%= fields_for "photos[]", photo do |pf| %>
      <%= pf.text_field :caption %>
      ... other photo fields
48 голосов
/ 10 июня 2009

ОБНОВЛЕНИЕ : Этот ответ относится к Rails 2, или если у вас есть специальные ограничения, требующие пользовательской логики. Простые случаи хорошо решаются с использованием fields_for, как обсуждалось в другом месте.

Rails не очень поможет вам в этом. Это идет вразрез со стандартными соглашениями о представлении, поэтому вам придется делать обходные пути в представлении, контроллере, даже маршрутах. Это не весело.

Ключевыми ресурсами для работы с многомодельными формами в Rails являются серия params-foo Стивена Чу , или, если вы на Rails 2.3, посмотрите Формы вложенных объектов

Становится намного проще, если вы определяете какой-то особый ресурс, который вы редактируете, например, Photoset. Photoset может быть реальной моделью ActiveRecord или просто фасадом, который принимает данные и выдает ошибки, как если бы это была модель ActiveRecord.

Теперь вы можете написать форму вида примерно так:

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

Ваша модель должна проверять каждую входящую фотографию ребенка и объединять их ошибки. Вы можете проверить хорошую статью о том, как включить валидации в любой класс . Это может выглядеть примерно так:

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

Используя модель фасада для фотосета, вы можете сохранять свой контроллер и просматривать логику простым и понятным, резервируя самый сложный код для выделенной модели. Этот код, вероятно, не выйдет из коробки, но, надеюсь, он даст вам некоторые идеи и укажет правильное направление для решения вашего вопроса.

18 голосов
/ 02 апреля 2013

У Rails есть способ сделать это - я не знаю, когда он был представлен, но в основном это описано здесь: http://guides.rubyonrails.org/form_helpers.html#using-form-helpers

Потребовалось немного возиться, чтобы правильно изменить конфигурацию для случая, когда нет родительского объекта, но это кажется правильным (это в основном то же самое, что и ответ Гамова, но более чистый и не учитывает "новые" записи смешано с «обновлением» записей):

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

В вашем контроллере вы получите хеш в params[:photos], где ключи - это идентификаторы фотографий, а значения - хэши атрибутов.

15 голосов
/ 22 июля 2010

Вы можете использовать синтаксис «имя модели []» для представления нескольких объектов.

В поле зрения используйте "photo []" в качестве названия модели.

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

Это заполнит поля ввода, как вы описали.

В вашем контроллере вы можете делать массовые обновления.

def update
  Photo.update(params[:photo].keys, params[:photo].values)
  ...
end 
8 голосов
/ 22 марта 2011

Действительно, как упоминал Турадж, Rack (Rails 3.0.5) не работает, если вы смешаете новые и существующие записи в ответе Глена. Вы можете обойти это, заставив fields_for работать вручную:

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

Это довольно уродливо, если вы спросите меня, но я нашел единственный способ отредактировать несколько записей, смешивая новые и существующие записи. Хитрость в том, что вместо того, чтобы иметь массив записей, хэш params получает массив хэшей (пронумерованных i, 0,1,2 и т. Д.) И идентификатор в хэше записи. Rails соответствующим образом обновит существующие записи и создаст новые.

Еще одно примечание: вам все еще нужно обрабатывать новые и существующие записи в контроллере отдельно (проверьте: id.present?)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...