无法更新嵌套属性

时间:2014-08-15 05:45:58

标签: ruby-on-rails

我正在尝试使用Rails 4.1构建一个小费用跟踪应用。当用户提交费用请求时,默认情况下将其状态标记为待处理。管理员必须批准该请求。我使用state_machine gem来做到这一点。

使用nested_form_for从费用显示页面添加注释,如下所示:

<%= nested_form_for (@expense) do |f| %>
      <div class="form-group">
        <%= f.label :state %><br />
        <%= f.collection_select :state, @expense.state_transitions, :event, :human_to_name, :include_blank => @expense.human_state_name, class: "form-control" %>
       </div>
       <%= f.fields_for :comments, @expense.comments.build do |comment| %>
        <div class="form-group">
          <%= comment.label :comment%>
          <%= comment.text_area :comment, class: "form-control" %>
        </div>
      <% end %>
        <%= f.submit "Submit", class: "btn btn-primary" %>
    <% end %>

控制器看起来像:

class ExpensesController < ApplicationController

    def new
        @expense = Expense.new
        @item = @expense.items.build
        @comment = @expense.comments.build
    end

    def show
        @expense = Expense.find(params[:id])
        @items = Item.where(:expense_id => @expense.id)
    end

    def update
        @expense = Expense.find(params[:id])
        if @expense.update(expense_params)
            if @expense.state == "approved"
                ExpenseMailer.expense_approved(@expense).deliver
                flash[:notice] = "Expense Report Updated"
                redirect_to expenses_path
            elsif  @expense.state = "rejected"
                ExpenseMailer.expense_declined(@expense).deliver
                flash[:notice] = "Expense Report Updated"
                redirect_to expenses_path
            end
        else
            render 'edit'
        end
    end

    private
    def expense_params
        params.require(:expense).permit(:claim, :department_id, :expense_type_id, :expense_attachment, :state, :notes, items_attributes: [:id, :description, :amount, :issue_date, :_destroy], comments_attributes:[:id, :comment, :expense_id])
    end

问题是,如果我在不改变下拉列表中的状态的情况下添加评论,我会认为状态无效&#39;错误和编辑页面显示。我只需点击更新按钮即可完成此操作。但是,评论还没有创建。另一方面,如果我更改状态并添加注释,则会显示注释而没有任何问题。

params的价值是:

Started PATCH "/expenses/14" for 127.0.0.1 at 2014-08-15 13:31:40 +0530
Processing by ExpensesController#update as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"MAEL2UYzos76NV6/eumHkXcpR2ge09wm6eOGQ+eEGCA=", "expense"=>{"state"=>"", "comments_attributes"=>{"0"=>{"comment"=>"vv"}}}, "commit"=>"Submit", "id"=>"14"}

状态机的费用模型如下:

state_machine initial: :pending do
        state :pending
        state :approved
        state :rejected

        event :approved do
            transition [:pending, :rejected] => :approved
        end
        event :rejected do
            transition [:pending, :approved] => :rejected
        end
   end

猜猜在构建评论属性时我犯了一些错误。有人能让我知道我必须在哪里做出改变吗?

拒绝的记录器信息:

Started GET "/expenses/17" for 127.0.0.1 at 2014-08-15 16:22:43 +0530
Processing by ExpensesController#show as HTML
  Parameters: {"id"=>"17"}
  [1m[35mExpense Load (0.2ms)[0m  SELECT  "expenses".* FROM "expenses"  WHERE "expenses"."id" = ? LIMIT 1  [["id", 17]]
  [1m[36mItem Load (0.1ms)[0m  [1mSELECT "items".* FROM "items"  WHERE "items"."expense_id" = ?[0m  [["expense_id", 17]]
  [1m[35mComment Load (0.2ms)[0m  SELECT "comments".* FROM "comments"  WHERE "comments"."expense_id" = ?  [["expense_id", 17]]
  Rendered expenses/show.html.erb within layouts/application (16.2ms)
Completed 200 OK in 45ms (Views: 42.8ms | ActiveRecord: 0.5ms)


Started PATCH "/expenses/17" for 127.0.0.1 at 2014-08-15 16:22:53 +0530
Processing by ExpensesController#update as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"MAEL2UYzos76NV6/eumHkXcpR2ge09wm6eOGQ+eEGCA=", "expense"=>{"state"=>"rejected", "comments_attributes"=>{"0"=>{"comment"=>"checking logger for rejected!"}}}, "commit"=>"Submit", "id"=>"17"}
  [1m[36mExpense Load (0.2ms)[0m  [1mSELECT  "expenses".* FROM "expenses"  WHERE "expenses"."id" = ? LIMIT 1[0m  [["id", 17]]
  [1m[35m (0.1ms)[0m  begin transaction
  [1m[36mSQL (8.1ms)[0m  [1mUPDATE "expenses" SET "state" = ?, "updated_at" = ? WHERE "expenses"."id" = 17[0m  [["state", "rejected"], ["updated_at", "2014-08-15 10:52:53.030676"]]
  [1m[35mSQL (0.2ms)[0m  INSERT INTO "comments" ("comment", "created_at", "expense_id", "updated_at") VALUES (?, ?, ?, ?)  [["comment", "checking logger for rejected!"], ["created_at", "2014-08-15 10:52:53.040889"], ["expense_id", 17], ["updated_at", "2014-08-15 10:52:53.040889"]]
  [1m[36m (4.2ms)[0m  [1mcommit transaction[0m
Redirected to http://localhost:3000/expenses
Completed 302 Found in 24ms (ActiveRecord: 12.8ms)

要获得批准的记录器信息:

Started GET "/expenses/16" for 127.0.0.1 at 2014-08-15 16:22:30 +0530
Processing by ExpensesController#show as HTML
  Parameters: {"id"=>"16"}
  [1m[35mExpense Load (0.3ms)[0m  SELECT  "expenses".* FROM "expenses"  WHERE "expenses"."id" = ? LIMIT 1  [["id", 16]]
  [1m[36mItem Load (0.2ms)[0m  [1mSELECT "items".* FROM "items"  WHERE "items"."expense_id" = ?[0m  [["expense_id", 16]]
  [1m[35mComment Load (0.3ms)[0m  SELECT "comments".* FROM "comments"  WHERE "comments"."expense_id" = ?  [["expense_id", 16]]
  Rendered expenses/show.html.erb within layouts/application (167.3ms)
Completed 200 OK in 244ms (Views: 213.7ms | ActiveRecord: 1.1ms)


Started PATCH "/expenses/16" for 127.0.0.1 at 2014-08-15 16:22:41 +0530
Processing by ExpensesController#update as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"MAEL2UYzos76NV6/eumHkXcpR2ge09wm6eOGQ+eEGCA=", "expense"=>{"state"=>"approved", "comments_attributes"=>{"0"=>{"comment"=>"checking logger!"}}}, "commit"=>"Submit", "id"=>"16"}
  [1m[36mExpense Load (0.2ms)[0m  [1mSELECT  "expenses".* FROM "expenses"  WHERE "expenses"."id" = ? LIMIT 1[0m  [["id", 16]]
  [1m[35m (0.1ms)[0m  begin transaction
  [1m[36mSQL (0.5ms)[0m  [1mUPDATE "expenses" SET "state" = ?, "updated_at" = ? WHERE "expenses"."id" = 16[0m  [["state", "approved"], ["updated_at", "2014-08-15 10:52:41.604580"]]
  [1m[35mSQL (0.5ms)[0m  INSERT INTO "comments" ("comment", "created_at", "expense_id", "updated_at") VALUES (?, ?, ?, ?)  [["comment", "checking logger!"], ["created_at", "2014-08-15 10:52:41.607555"], ["expense_id", 16], ["updated_at", "2014-08-15 10:52:41.607555"]]
  [1m[36m (4.0ms)[0m  [1mcommit transaction[0m
Redirected to http://localhost:3000/expenses
Completed 302 Found in 17ms (ActiveRecord: 5.3ms)

1 个答案:

答案 0 :(得分:1)

我不知道为什么您收到错误,但我会为state_machine / aasm提供一些建议。宝石

-

状态机

由于Rails是object-orientated,你必须了解这些state machine宝石是如何工作的 - 它们是建立一个状态机的电子方法的推断&#34; (用于预测有限的&#34;状态&#34;在电路中):

enter image description here

我试图证明的是,通过在您的应用中加入state machine,您实际上会指示对象的状态(&# 39; s 只是另一个属性)

目前,您将state模型的Comment属性视为属性,当它可以被视为对象本身时

-

<强>对象

请注意State Machine repo中的此功能:

enter image description here

注意这与 state 属性无关?

我认为您最好将state方法视为一种影响state_machine本身的方法。我会以几种方式做到这一点:

  
      
  1. 设置状态&#34;默认&#34; state_machine声明中的状态
  2.   
  3. 使用OOP原则验证 state 对象
  4.   
#app/models/expense.rb
Class Expense < ActiveRecord::Base
   state_machine :state, :initial => :pending do #-> sets the state to "pending" unless specified otherwise
   end
end

#app/controllers/expenses_controller.rb
Class ExpensesController < ApplicationController
   def update
      if @expense.approved?
          ...
      end
   end
end

-

<强>修正

关于您无法创建评论,我认为问题将是双重的

首先,您在视图中构建您的评论。除了任何事情,这是不好的做法(针对MVC) - 您最好在model内构建关联对象:

#app/models/expense.rb
Class Expense < ActiveRecord::Base
   def self.build id
      expense = (id.present?) self.find id : self.new
      expense.comments.build
      return expense
   end
end

这允许您执行以下操作:

#app/controllers/expenses_controller.rb
Class ExpensesController < ApplicationController
   def new
      @expense = Expense.build
   end

   def edit
      @expense = Expense.build params[:id]
   end
end

这基本上会为您的嵌套comments表单提供触发编辑所需的预构建嵌套对象。新方法(因此您不需要在视图中调用@expense.comments.build

关于非保存功能 - 我当然会看看你如何保存state属性。 我怀疑你没有正确传递属性(IE默认提交时你state param使用的值不正确

我建议使用以下内容:

  
      
  1. 从&#34;默认&#34;调查你的参数更新
  2.   
  3. state属性是否与属性的模型定义匹配?
  4.   
  5. 如果没有,那将是你的问题
  6.   

-

<强>更新

感谢您的更新

好的,问题似乎是如果state值是默认值,则不会传递它。我认为解决这个问题的方法是set a default value for the collection_select

  • 删除:include_blank => @expense.human_state_name
  • 替换为<%= f.collection_select :state, @expense.state_transitions, :event, :human_to_name, { selected: @expense.human_state_name}, class: "form-control" %>

更新2

由于state_machine使您能够跟踪&amp;成功转换后的fire实例方法,您可能希望执行以下操作:

#app/models/expense.rb
Class Expense < ActiveRecord::Base
    state_machine :state, :initial => :pending do
        state :pending
        state :approved
        state :rejected

        event :approve do
            transition [:pending, :rejected] => :approved
        end
        event :reject do
            transition [:pending, :approved] => :rejected
        end

        after_transition :on => :approved, :do => :send_approval_email
        after_transition :on => :rejected, :do => :send_rejection_email

        def send_approval_email 
           ExpenseMailer.expense_approved(self).deliver #-> might need to call outide of state_machine block
        end

        def send_rejection_email 
          ExpenseMailer.expense_declined(self).deliver
        end

    end
end

这将使您能够执行以下操作:

#app/controllers/expenses_controller.rb
Class ExpensesController < ApplicationController
   def update
        @expense = Expense.find params[:id]
        if @expense.update(expense_params)
            flash[:notice] = "Expense Report Updated"
            redirect_to expenses_path
        end
   end
end

顺便说一下,你需要改变你的事件&#34;为你的&#34;州和#34;提供不同的名字。根据上面面向对象的引用,您需要能够调用@object.approve

之类的内容