Rails中的表单对象
下面的示例代码是对表单对象的尝试的一个人为设想的示例,其中使用表单对象可能是过度的。 尽管如此:它显示了我遇到的问题:
我有两个模型: User
和Email
:
# app/models/user.rb class User < ApplicationRecord has_many :emails end
# app/models/user.rb class Email < ApplicationRecord belongs_to :user end
我想创建一个创建user
记录的表单对象 ,然后创建三个关联的email
记录。
这是我的表单对象类:
# 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
注意 : 表单对象的validation 。 如果user_form
的name
属性为空,或者如果email_text
属性在其email_forms
数组中的任何email_form
对象中保留为空,则认为user_form
无效。
为简洁起见:我将通过new
并create
利用user_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
最后:forms本身:
# app/views/users/new.html.erb New User
# app/views/users/_form.html.erb prohibited this user from being saved:
# MESSY, but couldn't think of a better way to do this...
表单确实呈现:
这是表单的html:
我去提交表格。 这是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"}
应该发生的是表单应该重新呈现并且没有任何持久性因为form_object无效:所有三个关联的电子邮件都不能为空。 但是:form_object认为它是有效的,它会在persist!
情况下爆炸persist!
UserForm
上的方法。 它突出显示了Email.create(user: user, email_text: email.email_text)
行并说:
未定义方法`email_text’for [“0”,{“email_text”=>“test_email_1”}]:数组
很明显,有几件事情发生了:嵌套validation似乎无法正常工作,我无法从params散列重建每封电子邮件。
我已经检查过的资源:
- 这篇文章似乎很有希望,但我无法让它工作。
- 我尝试使用virtus gem和reform-rails gem实现。 我也为这两个实现发布了待处理的问题: virtus尝试在这里然后改革rails尝试在这里 。
- 我试图插入
accepts_nested_attributes
,但是无法弄清楚如何利用表单对象以及嵌套表单对象(如此代码示例中)。 部分问题是has_many
和accepts_nested_attributes_for
似乎没有包含在ActiveModel::Model
。
有关获取此表单对象以执行预期操作的任何指导将非常感谢! 谢谢!
完整答案
楷模:
#app/models/user.rb class User < ApplicationRecord has_many :emails end #app/models/email.rb class Email < ApplicationRecord belongs_to :user end
控制器:
#app/controllers/users_controller.rb class UsersController < ApplicationController def index @users = User.all end def new @user_form = UserForm.new @user_form.emails = [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, {emails_attributes: [:email_text]}) end end
表格对象:
#app/forms/user_form.rb class UserForm include ActiveModel::Model attr_accessor :name, :emails validates :name, presence: true validate :all_emails_valid def emails_attributes=(attributes) @emails ||= [] attributes.each do |_int, email_params| email = EmailForm.new(email_params) @emails.push(email) end end def save if valid? persist! true else false end end private def persist! user = User.new(name: name) new_emails = emails.map do |email_form| Email.new(email_text: email_form.email_text) end user.emails = new_emails user.save! end def all_emails_valid emails.each do |email_form| errors.add(:base, "Email Must Be Present") unless email_form.valid? end throw(:abort) if errors.any? end end app/forms/email_form.rb class EmailForm include ActiveModel::Model attr_accessor :email_text, :user_id validates :email_text, presence: true end
浏览次数:
app/views/users/new.html.erb New User
<%= 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? %> <%= pluralize(user_form.errors.count, "error") %> prohibited this User from being saved:
<% user_form.errors.full_messages.each do |message| %> - <%= message %>
<% end %>
<% end %> <%= f.label :name %> <%= f.text_field :name %> <%= f.fields_for :emails do |email_form| %> <%= email_form.label :email_text %> <%= email_form.text_field :email_text %> <% end %> <%= f.submit %> <% end %>