【发布时间】:2017-08-09 05:47:55
【问题描述】:
我很难弄清楚如何创建一个 form_object,它为 has_many 与 virtus gem 关联创建多个关联对象。
下面是一个人为的例子,其中一个表单对象可能是矫枉过正的,但它确实显示了我遇到的问题:
假设有一个 user_form 对象创建一个 user 记录,然后是一对关联的 user_email 记录。以下是模型:
# models/user.rb
class User < ApplicationRecord
has_many :user_emails
end
# models/user_email.rb
class UserEmail < ApplicationRecord
belongs_to :user
end
我继续创建一个表单对象来表示用户表单:
# app/forms/user_form.rb
class UserForm
include ActiveModel::Model
include Virtus.model
attribute :name, String
attribute :emails, Array[EmailForm]
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)
# emails.each do |email_form|
# UserEmail.create(user: user, email: email_form.email_text)
# end
end
end
人们会在UserForm 类中注意到我有attribute :emails, Array[EmailForm]。这是尝试验证和捕获将为关联的user_email 记录保留的数据。这是user_email 记录的Embedded Value 表单:
# app/forms/email_form.rb
# Note: this form is an "Embedded Value" Form Utilized in user_form.rb
class EmailForm
include ActiveModel::Model
include Virtus.model
attribute :email_text, String
validates :email_text, presence: true
end
现在我将继续展示设置 user_form 的users_controller。
# app/controllers/users_controller.rb
class UsersController < ApplicationController
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 @user, notice: 'User was successfully created.'
else
render :new
end
end
private
def user_form_params
params.require(:user_form).permit(:name, {emails: [:email_text]})
end
end
new.html.erb:
<h1>New User</h1>
<%= render 'form', user_form: @user_form %>
还有_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>
<% unique_index = 0 %>
<% f.object.emails.each do |email| %>
<%= label_tag "user_form[emails][#{unique_index}][email_text]","Email" %>
<%= text_field_tag "user_form[emails][#{unique_index}][email_text]" %>
<% unique_index += 1 %>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
注意:如果有更简单、更传统的方式来显示此表单对象中 user_emails 的输入:请告诉我。我无法让fields_for 工作。如上图:我不得不手动写出name 属性。
好消息是表单会呈现:
表单的 html 对我来说看起来不错:
当上述输入提交时:这里是params hash:
Parameters: {"utf8"=>"✓", "authenticity_token"=>”abc123==", "user_form"=>{"name"=>"neil", "emails"=>{"0"=>{"email_text"=>"foofoo"}, "1"=>{"email_text"=>"bazzbazz"}, "2"=>{"email_text"=>""}}}, "commit"=>"Create User form"}
params 哈希对我来说看起来不错。
在日志中,我收到两个弃用警告,这让我认为 virtus 可能已过时,因此不再是 rails 中表单对象的有效解决方案:
弃用警告:方法 to_hash 已弃用,将在 Rails 5.1 中删除,因为
ActionController::Parameters不再继承自散列。使用这种已弃用的行为会暴露潜在的安全问题。如果您继续使用此方法,您可能会在您的应用程序中创建一个可被利用的安全漏洞。相反,请考虑使用以下未弃用的记录方法之一:http://api.rubyonrails.org/v5.0.2/classes/ActionController/Parameters.html(在 (pry):1 处调用新方法) 弃用警告:方法 to_a 已弃用,将在 Rails 5.1 中删除,因为ActionController::Parameters不再继承自散列。使用这种已弃用的行为会暴露潜在的安全问题。如果您继续使用此方法,您可能会在您的应用程序中创建一个可被利用的安全漏洞。相反,请考虑使用以下未弃用的记录方法之一:http://api.rubyonrails.org/v5.0.2/classes/ActionController/Parameters.html(在 (pry):1 处调用新方法) NoMethodError:预期 ["0", "foofoo"} 允许:true>] 响应 #to_hash 来自 /Users/neillocal/.rvm/gems/ruby-2.3.1/gems/virtus-1.0.5/lib/virtus/attribute_set.rb:196:in `coerce'
然后整个事情都出错了,并显示以下消息:
Expected ["0", <ActionController::Parameters {"email_text"=>"foofoo"} permitted: true>] to respond to #to_hash
我觉得我要么很接近并且缺少一些小东西才能让它工作,要么我意识到 virtus 已经过时并且不再可用(通过弃用警告)。
我查看的资源:
我确实尝试过使用相同的表单,但使用 reform-rails gem。我在那里也遇到了问题。这个问题是posted here。
提前致谢!
【问题讨论】:
标签: ruby-on-rails ruby virtus