我有一个艰难的时间搞清楚如何使一个form_object创建多个关联对象has_many
与virtus gem关联。Rails窗体对象与Virtus:has_many关联
下面是一个人为的例子,其中一个表单对象可能是矫枉过正,但它确实表明我遇到的问题:
比方说,有一个创建user
记录一user_form
对象,然后一对夫妇的相关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
One将在UserForm
类,我有attribute :emails, Array[EmailForm]
通知。这是试图验证和捕获将为相关的user_email
记录保留的数据。这里是Embedded Value
形式为user_email
记录:
# 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看起来不错对我说:
当上述输入提交:在这里是参数哈希:
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可能已经过期,并在轨道表单对象从而不再是一个有效的解决方案:
DEPRECATION WARNING: Method to_hash is deprecated and will be removed in Rails 5.1, as
ActionController::Parameters
no longer inherits from hash. Using this deprecated behavior exposes potential security problems. If you continue to use this method you may be creating a security vulnerability in your app that can be exploited. Instead, consider using one of these documented methods which are not deprecated: http://api.rubyonrails.org/v5.0.2/classes/ActionController/Parameters.html (called from new at (pry):1) DEPRECATION WARNING: Method to_a is deprecated and will be removed in Rails 5.1, asActionController::Parameters
no longer inherits from hash. Using this deprecated behavior exposes potential security problems. If you continue to use this method you may be creating a security vulnerability in your app that can be exploited. Instead, consider using one of these documented methods which are not deprecated: http://api.rubyonrails.org/v5.0.2/classes/ActionController/Parameters.html (called from new at (pry):1) NoMethodError: Expected ["0", "foofoo"} permitted: true>] to respond to #to_hash from /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。
在此先感谢!
请接受修改,我会很乐意将您的答案标记为已接受的答案。稍作修改以使验证按预期工作,然后为了方便:为所有主要部分提供代码。我想给你信贷,因为你张贴的答案让我通过了我被卡住的部分。 – Neil
应该指出的是,人们应该考虑使用virtu gem为你的表单对象带来的好处。机会是:你可以很容易地创建你的表单对象,而不需要将virt-gem依赖项添加到你的项目中。 [见Rails窗体对象实现在这里](http://stackoverflow.com/questions/42930117/form-objects-in-rails/43024542#43024542) – Neil
感谢您的答案。这正是我一直在寻找的。 虽然我是Rails newb。有人可以向我解释为什么我们需要定义'emails_attributes =(属性)'。这是什么调用?我发现如果这个方法没有定义,那么'@user_form.emails = [EmailForm.new,EmailForm.new,EmailForm.new]'不能按预期工作。 Rails中的这个魔术发生在哪里? – user3574603