rails form_with form_for form ruby-on-rails forms post

ruby on rails - form_with - MĂșltiples objetos en una forma Rails



nested form rails (5)

ACTUALIZACIÓN : esta respuesta se aplica a Rails 2, o si tiene restricciones especiales que requieren lógica personalizada. Los casos fáciles están bien tratados usando fields_for como se discutió en otra parte.

Rails no te va a ayudar mucho para hacer esto. Va en contra de las convenciones de vista estándar, por lo que tendrá que hacer soluciones en la vista, el controlador e incluso las rutas. Eso no es divertido.

Los recursos clave para tratar con formas multimodelo al estilo de Rails son las series params-foo de Stephen Chu , o si está en Rails 2.3, consulte Formularios de Objetos Anidados

Se vuelve mucho más fácil si define algún tipo de recurso singular que está editando, como un Photoset. Un Photoset podría ser un tipo de modelo de ActiveRecord real, o simplemente puede ser una fachada que acepta datos y arroja errores como si fuera un modelo de ActiveRecord.

Ahora puede escribir una forma de vista algo así:

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

Su modelo debe validar cada foto secundaria que entre y agregar sus errores. Es posible que desee consultar un buen artículo sobre cómo incluir Validaciones en cualquier clase . Podría verse algo como esto:

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

Al usar un modelo de fachada para el Photoset, puede mantener el controlador y la lógica de visualización simple y directa, reservando el código más complejo para un modelo dedicado. Este código probablemente no se ejecutará de manera predeterminada, pero con suerte le dará algunas ideas y lo orientará en la dirección correcta para resolver su pregunta.

Quiero editar varios elementos de mi foto modelo en una sola forma. No estoy seguro de cómo presentar correctamente y PUBLICAR esto con un formulario, así como también cómo reunir los elementos en la acción de actualización en el controlador.

Esto es lo que quiero:

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

Los parámetros son solo un ejemplo, como dije antes: no estoy seguro de la mejor manera de PUBLICAR estos valores en esta forma.

En el controlador quiero algo como esto:

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


De hecho, como mencionó Turadg, Rack (Rails 3.0.5) falla si se mezclan registros nuevos y existentes en la respuesta de Glen. Puede solucionar esto haciendo fields_for work manualmente:

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

Esto es bastante feo si me preguntas, pero es la única forma en que encontré para editar múltiples registros al mezclar registros nuevos y existentes. El truco aquí es que en lugar de tener una matriz de registros, el hash de params obtiene una matriz de hashes (numerados con i, 0,1,2, etc.) Y la identificación en el hash de registro. Rails actualizará los registros existentes en consecuencia y creará los nuevos.

Una nota más: todavía necesita procesar los registros nuevos y existentes en el controlador por separado (verifique si: id.present?)


En Rails 4, solo esto

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


Puede usar la sintaxis "model name []" para representar múltiples objetos.

A la vista, use "photo []" como nombre de modelo.

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

Esto rellenará los campos de entrada tal como lo describió.

En su controlador, puede hacer actualizaciones masivas.

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


Rails tiene una forma de hacerlo: no sé cuándo se presentó, pero básicamente se describe aquí: http://guides.rubyonrails.org/form_helpers.html#using-form-helpers

Se tardó un poco de tiempo en alterar la configuración correctamente para el caso donde no hay un objeto principal, pero parece ser correcto (básicamente es lo mismo que la respuesta de gamov, pero más limpio y no permite registros "nuevos" mezclados con los registros de "actualización"):

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

En su controlador, terminará con un hash en params[:photos] , donde las claves son identificadores con foto, y los valores son valores hash de atributo.