gpt4 book ai didi

ruby-on-rails - 使用 Virtus 的 Rails 表单对象 : has_many association

转载 作者:数据小太阳 更新时间:2023-10-29 07:58:41 26 4
gpt4 key购买 nike

我很难弄清楚如何制作一个 form_object,它为 has_manyvirtus 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 属性。

好消息是表单确实呈现了:

rendered form

我觉得表单的 html 没问题:

html of the form

提交上述输入时:这是参数散列:

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 中表单对象的有效解决方案:

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, 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) 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 .

提前致谢!

最佳答案

我只是将 user_form.rb 中 user_form_params 的 emails_attributes 设置为 setter 方法。这样您就不必自定义表单域。

完整答案:

模型:

#app/modeles/user.rb
class User < ApplicationRecord
has_many :user_emails
end

#app/modeles/user_email.rb
class UserEmail < ApplicationRecord
# contains the attribute: #email
belongs_to :user
end

表单对象:

# app/forms/user_form.rb
class UserForm
include ActiveModel::Model
include Virtus.model

attribute :name, String

validates :name, presence: true
validate :all_emails_valid

attr_accessor :emails

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|
UserEmail.new(email: email.email_text)
end
user.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
# "Embedded Value" Form Object. Utilized within the user_form object.
class EmailForm
include ActiveModel::Model
include Virtus.model

attribute :email_text, String

validates :email_text, presence: true
end

Controller :

# app/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/views/users/new.html.erb
<h1>New User</h1>
<%= render 'form', user_form: @user_form %>


#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>


<%= f.fields_for :emails do |email_form| %>
<div class="field">
<%= email_form.label :email_text %>
<%= email_form.text_field :email_text %>
</div>
<% end %>


<div class="actions">
<%= f.submit %>
</div>
<% end %>

关于ruby-on-rails - 使用 Virtus 的 Rails 表单对象 : has_many association,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42861425/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com