Formar objetos en rieles
El siguiente código de ejemplo es un ejemplo artificial de un intento de un objeto de formulario en el que probablemente sea excesivo utilizar un objeto de formulario. No obstante: muestra el problema que estoy teniendo:
Tengo dos modelos: unUser
y unEmail
:
# app/models/user.rb
class User < ApplicationRecord
has_many :emails
end
# app/models/user.rb
class Email < ApplicationRecord
belongs_to :user
end
Quiero crear unobjeto de forma que crea unuser
grabar, y luego crea tres asociadosemail
archivos.
Aquí están mis clases de objetos de formulario:
# app/forms/user_form.rb
class UserForm
include ActiveModel::Model
attr_accessor :name, :email_forms
validates :name, presence: true
def save
if valid?
persist!
true
else
false
end
end
private
def persist!
puts "The Form is VALID!"
puts "I would proceed to create all the necessary objects by hand"
user = User.create(name: name)
email_forms.each do |email|
Email.create(user: user, email_text: email.email_text)
end
end
end
# app/forms/email_form.rb
class EmailForm
include ActiveModel::Model
attr_accessor :email_text, :user_id
validates :email_text, presence: true
def save
if valid?
persist!
true
else
false
end
end
private
def persist!
puts "The Form is VALID!"
# DON'T THINK I WOULD PERSIST DATA HERE
# INSTEAD DO IT IN THE user_form
end
end
Aviso: elvalidaciones en los objetos de formulario. UNAuser_form
se considera no válido si esname
el atributo está en blanco o si elemail_text
el atributo se deja en blanco para cualquiera de losemail_form
objetos dentro esemail_forms
formación.
Por brevedad: solo voy a pasar por elnew
ycreate
acción de utilizar eluser_form
:
# app/controllers/user_controller.rb
class UsersController < ApplicationController
def new
@user_form = UserForm.new
@user_form.email_forms = [EmailForm.new, EmailForm.new, EmailForm.new]
end
def create
@user_form = UserForm.new(user_form_params)
if @user_form.save
redirect_to users_path, notice: 'User was successfully created.'
else
render :new
end
end
private
def user_form_params
params.require(:user_form).permit(:name, {email_forms: [:_destroy, :id, :email_text, :user_id]})
end
end
Por último: la forma misma:
# app/views/users/new.html.erb
<h1>New User</h1>
<%= render 'form', user_form: @user_form %>
<%= link_to 'Back', users_path %>
# app/views/users/_form.html.erb
<%= form_for(user_form, url: users_path) do |f| %>
<% if user_form.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(user_form.errors.count, "error") %> prohibited this user from being saved:</h2>
<ul>
<% user_form.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %>
<%= f.text_field :name %>
</div>
# MESSY, but couldn't think of a better way to do this...
<% unique_index = 0 %>
<% user_form.email_forms.each do |email_form| %>
<div class="field">
<%= label_tag "user_form[email_forms][#{unique_index}][email_text]", "Email Text" %>
<%= text_field_tag "user_form[email_forms][#{unique_index}][email_text]" %>
</div>
<% unique_index += 1 %>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
El formulario representa:
Y aquí está el formulario html:
Voy a enviar el formulario. Aquí está el hash de params:
Parameters: {"utf8"=>"✓", "authenticity_token"=>”abc123==", "user_form"=>{"name"=>"neil", "email_forms"=>{"0"=>{"email_text"=>"test_email_1"}, "1"=>{"email_text"=>"test_email_2"}, "2"=>{"email_text"=>""}}}, "commit"=>"Create User form"}
Lo que debería suceder es que el formulario debe volver a representarse y no persistir nada porque form_object no es válido: los tres correos electrónicos asociados NO deben estar en blanco. Sin embargo: form_object cree que es válido y explota en elpersist!
método en elUserForm
. Destaca elEmail.create(user: user, email_text: email.email_text)
línea y dice:
método indefinido `email_text 'para [" 0 ", {" email_text "=>" test_email_1 "}]: matriz
Claramente, están sucediendo un par de cosas: las validaciones anidadas parecen no funcionar, y tengo problemas para reconstruir cada uno de los correos electrónicos del hash de params.
Recursos que ya he examinado:
Este artículo Parecía prometedor pero estaba teniendo problemas para que funcione.He intentado una implementación con la gema virtus y la gema reform-rails. Tengo preguntas pendientes publicadas para ambas implementaciones también:intento de virtus aquí y entoncesintento de reforma-rieles aquí.He intentado enchufarmeaccepts_nested_attributes
, pero tenía problemas para descubrir cómo utilizar eso con un objeto de formulario, así como con un objeto de formulario anidado (como en este ejemplo de código). Parte del problema era quehas_many
yaccepts_nested_attributes_for
no parecen estar incluidos enActiveModel::Model
.¡Cualquier orientación sobre cómo hacer que este objeto de formulario haga lo que se espera sería muy apreciada! ¡Gracias!