【问题标题】:Refactor code to avoid circular logic in controller重构代码以避免控制器中的循环逻辑
【发布时间】:2014-06-26 01:02:49
【问题描述】:

情况

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

期望的结束状态

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

当前的 hacky 解决方案和复杂性

  • 目前,我正在分阶段检查,1) 项目中有数量吗?如果不是,那么无论用户可能为 Request 属性设置了什么,页面都会重新呈现(即,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"]的视图代码的sn-p

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

【问题讨论】:

  • 控制器看起来缺少它的一部分。 “items_to_be_saved”从未在任何地方使用,并且数量内的“item_to_be_saved”似乎是一个错字。
  • 只是一个快速的错字。固定!
  • 您正在构建 items_to_be_saved 但它从未使用过。是否应将 transactions_to_be_saved 替换为 items_to_be_saved?
  • 是的,抱歉打字太快了!

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


【解决方案1】:

验证

当你提到要同时返回所有错误时,基本上意味着你需要使用 Rails 的validations functionality

这会填充@model.errors 对象,然后您可以像这样在form 上使用它:

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

我认为您的问题是您正在尝试使用控制器中的验证。这既不利于MVC principles 又不利于模块化编程。您需要的功能可通过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

只有在创建请求时才会触发。即如果您的请求由于某种原因(验证问题)失败,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

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

【讨论】:

  • 你知道我昨晚睡着了,以为 Rich 可能会在我醒来时得到这个 =)。不过我确实有一个问题。这对我来说很难的原因......因为我考虑过使用accepted_nested_attributes 是因为items 并不是一个真正的属性。就像一个请求有许多项目,但一个项目除了它的名称之外没有其他属性。它的名字是#{thing} 我已经在表单中调用了,用户唯一输入的是quantity 变量。在那种情况下这行得通吗?
  • 嗨詹姆斯,你太客气了!我只是每天花时间在这里磨练我的技能。当您说items 不是真正的属性时-您会感到困惑。 Accepts nested attributes 接受子 model 的属性,这意味着您发送哪个 attributes 并不重要,只要 Item 模型是他们要去的地方
  • 我很清楚,accepts nested attributes 即使我没有发送任何属性也可以工作?用户输入的数据不是子模型中的属性,它是代表我要创建的新子记录数的数量。
猜你喜欢
  • 2015-08-11
  • 2012-06-24
  • 2017-06-16
  • 2012-10-16
  • 2019-06-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多