Подтвердить что ты не робот

Несколько объектов в форме Rails

Я хочу редактировать несколько элементов моей фото модели в одной форме. Я не уверен, как правильно представлять и 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>

Параметры - это всего лишь пример, как я уже говорил выше: я не уверен в наилучшем способе POST этих значений в этой форме.

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

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

Ответ 1

В 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

Ответ 2

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

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

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

Это становится намного проще, если вы определяете какой-то особый ресурс, который вы редактируете, например, Photoset. A 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

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

Ответ 3

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

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

<%= 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], где ключи - это идентификаторы фотографий, а значения - хэши-атрибуты.

Ответ 4

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

В поле зрения, используйте "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 

Ответ 5

В самом деле, как сказал Турадж, 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 %>

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

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