【问题标题】:Rails form object with reform-rails with collections not working or validatingRails 使用改革轨道形成对象,集合不起作用或验证
【发布时间】:2017-03-17 20:12:55
【问题描述】:

我正在使用reform-rails gem 以便在我的rails 项目中使用表单对象。

我意识到表单对象对于我在下面使用的示例代码来说可能是多余的,但它是出于演示目的。

在我创建user 的表单中,与该用户记录相关联的是两个user_emails

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

# models/user_email.rb
class UserEmail < ApplicationRecord
  belongs_to :user
end

请注意,我没有在 User 模型中使用 accepts_nested_attributes_for :user_emails。在我看来,表单对象的要点之一是它可以帮助您摆脱使用accepts_nested_attributes_for,所以这就是我试图在没有它的情况下这样做的原因。我从this video 那里得到了这个想法,它谈到了重构胖模型。我有指向视频中关于表单对象部分的链接,他表达了他多么不喜欢accepts_nested_attributes_for

然后我继续创建我的user_form

# app/forms/user_form.rb
class UserForm < Reform::Form
  property :name
  validates :name, presence: true

  collection :user_emails do
    property :email_text
    validates :email_text, presence: true
  end
end

所以user_form 对象包装了一个user 记录,然后是几个与该user 记录关联的user_email 记录。在useruser_email 记录上有表单级验证,此表单包含:

  • user#name 必须有一个值
  • 每个user_email#email_text 都必须有一个值

如果表单是有效的:那么它应该创建一个user 记录,然后是一对关联的user_email 记录。如果表单无效:那么它应该重新呈现带有错误消息的表单。

我将展示到目前为止我在控制器中拥有的东西。为简洁起见:仅显示new 操作和create 操作:

# app/controllers/users_controller.rb
class UsersController < ApplicationController

  def new
    user = User.new
    user.user_emails.build
    user.user_emails.build
    @user_form = UserForm.new(user)
  end

  def create
    @user_form = UserForm.new(User.new(user_params))
    if @user_form.valid?
      @user_form.save
      redirect_to users_path, notice: 'User was successfully created.'
    else
      render :new
    end
  end

  private
    def user_params
      params.require(:user).permit(:name, user_emails_attributes: [:_destroy, :id, :email_text])
    end
end

最后:表单本身:

# app/views/users/_form.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>
  <% f.fields_for :user_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 %>

作为测试:这是输入值的表单:

现在我继续提交。应该发生的是应该存在验证错误,因为必须存在第二封电子邮件的值。但是,在这里提交时是日志:

Parameters: {"utf8"=>"✓", "authenticity_token"=>”123abc==", "user"=>{"name"=>"neil", "user_emails_attributes"=>{"0"=>{"email_text"=>"email_test1"}, "1"=>{"email_text"=>""}}}, "commit"=>"Create User"}

ActiveModel::UnknownAttributeError (unknown attribute 'user_emails_attributes' for User.):

所以我的表单对象有一些问题。

我怎样才能让这个表单对象工作?是否可以使用reform_rails 并使用accepts_nested_attributes 让这个表单对象工作?最终:我只想让表单对象工作。

除了reform-rails 文档之外,我已经探索过一些资源:

我第一次尝试创建表单对象是使用virtus gem,但我似乎也无法让那个对象工作。我也为该实现发布了stackoverflow question

【问题讨论】:

  • 您可能想检查在它们从未被持久化的情况下,Reform 如何处理来自参数的集合对象的实例化和验证。例如,accepts_nested_attributes_for 具有拒绝空白的选项。

标签: ruby-on-rails ruby reform


【解决方案1】:

完整答案:

型号:

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

# app/models/user_email.rb
class UserEmail < ApplicationRecord
  belongs_to :user
end

表单对象:

# app/forms/user_form.rb
# if using the latest version of reform (2.2.4): you can now call validates on property 
class UserForm < Reform::Form
  property :name, validates: {presence: true}

  collection :user_emails do
    property :email_text, validates: {presence: true}
  end
end

控制器:

# app/controllers/users_controller.rb
class UsersController < ApplicationController
  before_action :user_form, only: [:new, :create]

  def new 
  end

  # validate method actually comes from reform this will persist your params to the Class objects
  # you added to the UserForm object. 
  # this will also return a boolean true or false based on if your UserForm is valid. 
  # you can pass either params[:user][:user_emails] or params[:user][user_email_attributes]. 
  # Reform is smart enough to pick up on both.
  # I'm not sure you need to use strong parameters but you can. 

  def create    
    if @user_form.validate(user_params)
      @user_form.save
      redirect_to users_path, notice: 'User was successfully created.'
    else
      render :new
    end
  end

  private

  # call this method in a hook so you don't have to repeat
  def user_form
    user = User.new(user_emails: [UserEmail.new, UserEmail.new])
    @user_form ||= UserForm.new(user)
  end 

  # no need to add :id in user_emails_attributes
  def user_params
    params.require(:user).permit(:name, user_emails_attributes: [:_destroy, :email_text])
   end
 end

表格:

# 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>
  <%= f.fields_for :user_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 %>

【讨论】:

  • 哇!非常感谢!请批准我的小修改,其中包括所有部分(为了完成),我很乐意将您的答案标记为已接受的答案!
  • 我也会在virtus implementation question 上解决您的问题。如果您愿意的话:随时查看我的raw rails 5 implementation question on form objects。不幸的是,我几乎没有声望点,所以我不能提供太多的赏金。
  • 你救了我的命:D
【解决方案2】:

终于搞定了!!!

首先,我无法将集合保存在 Rails 5 上。我创建了一个 4.2.6 并且它适用于我们的盒子。我建议您在改革 gem 的 github 存储库页面上创建一个问题。

所以,这是工作代码:

models/user.rb

class User < ActiveRecord::Base
  has_many :user_emails
end

models/user_email.rb

class UserEmail < ActiveRecord::Base
  belongs_to :user
end

user_form.rb

class UserForm < Reform::Form

  property :name
  validates :name, presence: true

  collection :user_emails, populate_if_empty: UserEmail do
    property :email_text
    validates :email_text, presence: true
  end
end

populate_if_empty 在进行验证时很重要。

以及控制器创建方法:

def create
  @user_form = UserForm.new(User.new)
  if @user_form.validate(user_params)
    @user_form.save
    redirect_to users_path, notice: 'User was successfully created.'
  else
    render :new
  end
end

这将validates您的用户模型以及任何嵌套关联。

你有它!干模型、验证和模型和关联的保存。

我希望这会有所帮助!

【讨论】:

    猜你喜欢
    • 2022-01-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多