Comprender cómo se transmite información entre el cliente y el servidor cuando trabajamos con modelos relacionados es fundamental para construir formularios robustos en Rails. La clave está en una convención del HTML que muchas veces pasa desapercibida: el atributo name de los inputs. A continuación se explica paso a paso cómo funciona esta mecánica y por qué no es magia, sino reglas claras de nomenclatura.
¿Cómo se relaciona un formulario HTML con un modelo de Rails?
Todo parte de una taxonomía sencilla. Un formulario contiene etiquetas, inputs y un botón de envío (submit). Los inputs son el vehículo para transportar datos del navegador al servidor, y el submit dispara la acción de envío.
Cuando conectamos esto con Rails, el protagonista es el atributo name de cada input [0:55]. Este atributo indica a qué campo del modelo hace referencia cada dato. Por ejemplo, si tenemos un modelo llamado usuario con dos campos —nombre y apellido—, los inputs generados con SimpleForm lucirían así:
name="usuario[nombre]" → referencia al campo nombre.
name="usuario[apellido]" → referencia al campo apellido.
Si el usuario escribe "Johan" en nombre y "Tique" en apellido, los params que recibe Rails al momento del envío forman un hash de clave-valor [1:25]:
ruby
{ usuario: { nombre: "Johan", apellido: "Tique" } }
Esta estructura es la que el controlador procesa para crear o actualizar registros.
¿Qué ocurre cuando un modelo tiene asociaciones anidadas?
La complejidad aparece cuando el modelo principal contiene colecciones de otros modelos. Imaginemos que un usuario tiene muchos teléfonos (has_many), y cada teléfono posee dos campos: número y tipo [1:50].
¿Se puede colocar un formulario dentro de otro?
La respuesta directa es no. HTML no permite anidar formularios y esperar que todo funcione correctamente [2:15]. Lo que se hace en su lugar es aplicar convenciones en el atributo name para que tanto el cliente como el servidor entiendan que existe una relación de anidamiento.
¿Cómo se construye la convención de nombres?
Supongamos dos registros de teléfono: uno de tipo "casa" y otro de tipo "oficina". La convención sigue este patrón [2:35]:
name="usuario[telefonos_attributes][0][numero]" → número del primer teléfono.
name="usuario[telefonos_attributes][0][tipo]" → tipo del primer teléfono.
name="usuario[telefonos_attributes][1][numero]" → número del segundo teléfono.
name="usuario[telefonos_attributes][1][tipo]" → tipo del segundo teléfono.
El identificador único (0, 1, etc.) es imprescindible porque cada línea del formulario solo aporta la información de un campo. Sin ese índice, Rails no podría agrupar los campos que pertenecen al mismo registro [2:55].
¿Cómo lucen los params con modelos anidados en el servidor?
Una vez que el formulario se envía, Rails recibe una estructura de hash que refleja la relación [3:40]:
ruby
{
usuario: {
nombre: "Johan",
apellido: "Tique",
telefonos_attributes: {
"0" => { numero: "55555", tipo: "casa" },
"1" => { numero: "66666", tipo: "oficina" }
}
}
}
Esta representación permite que el controlador, mediante accepts_nested_attributes_for, procese tanto el modelo principal como sus asociaciones en una sola operación.
Cuando herramientas como SimpleForm y la gema Cocoon generan dinámicamente campos en el navegador, lo que hacen internamente es crear inputs con esta misma convención de nombres [3:55]. No hay nada oculto: se agregan nodos HTML que siguen las reglas descritas, permitiendo añadir o eliminar registros asociados sin recargar la página.
Entender esta mecánica resulta esencial antes de trabajar con Selectize y Cocoon en conjunto, ya que ambas librerías operan sobre la misma base de formularios anidados para habilitar comportamientos dinámicos en la interfaz. Si tienes dudas sobre cómo se aplica esta convención en tus propios modelos, comparte tu caso y lo revisamos juntos.