重构代码以避免控制器中的循环逻辑

时间:2014-06-26 01:02:50

标签: ruby-on-rails forms validation ruby-on-rails-4 nested-forms

场合

  • 一种表单,允许用户选择他们想要请求的多个项目数量
  • 此表单POST到两个模型,一个父项:Request和child:Items。
  • 提交后,会创建一个请求,但最多会创建几个项目,具体取决于指示的数量
  • 为了解决这个问题,我有两套参数,一组用于物品,一组用于请求

所需的最终状态

  • 我不希望在没有请求的情况下创建项目,也不希望在没有项目
  • 的情况下创建请求
  • 一旦重新呈现页面,表单中出现的所有错误(无论是否选择至少一个项目,或者Request对象的属性中的错误)都会一起显示给用户;即,我想一起完成所有错误检查

当前的hacky解决方案&并发症

  • 目前,我分阶段检查,1)项目中是否有数量?如果不是,则无论用户可能为Request属性添加了什么,都重新呈现页面(即,请求的所有属性都将丢失,以及将显示的任何验证错误)。 2)一旦第一阶段通过,模型验证就会启动,如果失败,则再次重新渲染新页面

我花了很长时间思考这个问题,并没有想到任何优雅。对hacky解决方案感到满意,但是会喜欢更聪明的人的见解!

控制器代码(现在很胖,稍后会修复)

def create

request_params
@requestrecord = @signup_parent.requests.build

if @itemparams.blank?
  @requestrecord.errors[:base] = "Please select at least one item"
  render 'new'
else
  @requestrecord = @signup_parent.requests.create(@requestparams)
  if @requestrecord.save
    items_to_be_saved = []
    @itemparams.each do |item, quantity|
    quantity = quantity.to_i
      quantity.times do
        items_to_be_saved << ({:request_id => 0, :name => item })
      end
    end
    Item.create items_to_be_saved
    flash[:success] = "Thanks!"
    redirect_to action: 'success'
  else
    render 'new'
  end
end
end

def request_params
  @requestparams = params.require(:request).permit(:detail, :startdate, :enddate) 
  @itemparams = params["item"]
  @itemparams = @transactionparams.first.reject { |k, v| (v == "0") || (v == "")}
end

如果它有用,则生成params["item"]的视图代码片段

          <% itemlist.each do |thing| %>
            <%= number_field_tag "item[][#{thing}]", :quantity, min: 0, placeholder: 0 %>
            <%= label_tag thing %>
            </br>
          <% end %>
          <!-- itemlist is a variable in the controller that is populated with a list of items -->

1 个答案:

答案 0 :(得分:0)

<强>验证

当你提到你想要同时返回所有错误时,它基本上意味着你需要使用Rails&#39; validations functionality

这会填充@model.errors对象,您可以在form上使用此对象:

<% if @model.errors.any? %>
  <ul>
    <% @model.errors.full_messages.each do |msg| %>
      <li><%= msg %></li>

我认为您的问题是您尝试在控制器中使用验证。这是针对MVC principles&amp;通常不利于编程模块化。 validations功能提供了您需要的功能:


使用inverse_of创建一些条件验证可能会使您受益;或使用reject_if

<强> reject_if

#app/models/request.rb
Class Request < ActiveRecord::Base
   accepts_nested_attributes_for :items, reject_if: proc { |attributes| attributes['an_item_param'].blank? #-> attributes are the "item" attributes }
end

只有在创建请求时才会触发此操作。 I.E如果您的请求由于某种原因(验证问题)失败,则accepts_nested_attributes_for方法将无法运行,并使用附加的errors

返回您的对象

这主要用于验证嵌套资源(除非item属性已填充等,否则您无法保存title

-

<强> inverse_of

#app/models/request.rb
Class Request < ActiveRecord::Base
   has_many :items, inverse_of: :request
   accepts_nested_attributes_for :items
end

#app/models/item.rb
Class Item < ActiveRecord::Base
   belongs_to :request, inverse_of: :items
   validates :title, presence: true, unless: :draft?

   private

   def draft?
       self.request.draft #-> an example we've used before :)
   end
end

这更适用于特定于模型的验证;允许您确定具体条件。如果我们想要保存草稿等,我们会使用它