Ruby on rails 更新嵌套属性时出现问题

Ruby on rails 更新嵌套属性时出现问题,ruby-on-rails,Ruby On Rails,我正在尝试使用Rails 4.1构建一个小型费用跟踪应用程序。当用户提交费用申请时,其状态默认标记为挂起。管理员必须批准该请求。我用状态机gem来做这个 使用嵌套表单从费用显示页面添加注释,如下所示: <%= nested_form_for (@expense) do |f| %> <div class="form-group"> <%= f.label :state %><br /> <%= f.

我正在尝试使用Rails 4.1构建一个小型费用跟踪应用程序。当用户提交费用申请时,其状态默认标记为挂起。管理员必须批准该请求。我用状态机gem来做这个

使用嵌套表单从费用显示页面添加注释,如下所示:

<%= 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
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)
用于批准的记录器信息:

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)
我不知道你为什么会出错,但我会给你一些关于/gems的想法

--

状态机

由于Rails是,您必须了解这些GEM是如何工作的——它们是建立“状态机”(用于预测电路中的有限“状态”)的电子学方法的外推:

我试图用它来演示的是,通过在应用程序中包含
状态机
,实际上是在指示对象的状态(它不是而只是另一个属性)

目前,您正在将
注释
模型的
状态
属性视为一个属性,而该属性本身可以被视为一个对象

--

对象

请注意状态机repo中的此功能:

注意这与state属性无关吗

我认为你最好按照它的本来面目来对待
状态
方法-一种影响
状态机
本身的方法。我会通过几种方式来实现这一点:

  • 在状态机器声明中设置状态“默认”状态
  • 使用OOP原则验证状态对象
  • 这允许您执行以下操作:

    #app/controllers/expenses_controller.rb
    Class ExpensesController < ApplicationController
       def new
          @expense = Expense.build
       end
    
       def edit
          @expense = Expense.build params[:id]
       end
    end
    
    #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
    
    这将使您能够执行以下操作:

    #app/controllers/expenses_controller.rb
    Class ExpensesController < ApplicationController
       def new
          @expense = Expense.build
       end
    
       def edit
          @expense = Expense.build params[:id]
       end
    end
    
    #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
    
    #app/controllers/expenses_controller.rb
    类别费用控制器<应用程序控制器
    def更新
    @费用=费用。查找参数[:id]
    如果@expense.update(费用参数)
    flash[:通知]=“费用报告已更新”
    将\u重定向到\u路径
    结束
    结束
    结束
    

    顺便说一句,您需要更改“事件”以使“状态”具有不同的名称。根据我上面提到的面向对象的参考资料,您需要能够调用类似的
    @object.approve

    我已尝试重新缩进您的代码,请检查其correct@mu無 是的,没错。非常感谢。顺便说一句,
    state\u机器
    的维护不如
    aasm\u状态
    ——工作方式非常相似,只是后者更新更为频繁。在强参数方法中,你的
    状态
    属性在哪里?@RichPeck我使用的是一个积极维护的状态机器叉。state属性位于强参数中。“这是名单上的第五个。”里克·佩克谢谢你的精彩回答。我已经接受了您建议的控制器方法,我将花一些时间详细了解您关于状态机的建议。模型中的state属性是:state。根据您的建议,我在只提交评论而不更改状态时查看了参数,state的值为空。如何使其保持原始状态?根据参数为您更新答案。提交时,您的
    状态
    属性似乎为空。注释现在已保存。但是,在设置默认值后,“状态”下拉列表现在只显示下拉列表中的一个值。例如,如果以前的状态最初是“已批准”,则显示页面中的下拉列表显示“已拒绝”,反之亦然。我使用了模型方法,电子邮件不会发送。事实上,我已经尝试过类似的东西,但几天前无法让它工作,并在这里提出了一个问题。所以,我尝试了所有流行的状态机——SM、assm、transition、workflow等等。没有一个模型方法被触发。所以,我把它们都扔掉了。将当前_状态添加到费用表并开始保存状态值。编写了一个条件循环,检查state==当前状态,在这种情况下不会发送电子邮件。非常感谢你的努力。你花了将近半天的时间帮我弄明白了。非常感谢!