【问题标题】:Rails form_for results in POST instead of PUT when trying to editRails form_for 在尝试编辑时导致 POST 而不是 PUT
【发布时间】:2016-02-03 23:19:05
【问题描述】:

我正在使用 Rails 4 并出现以下错误。

路由错误
没有路线匹配 [POST] "/logs/1/meals/13/edit

我正在使用 :meal 传递模型对象的 form_for,并且编辑页面正在正确呈现。但是,Rails 似乎并没有检查餐对象是否已经保存,所以它一直尝试将表单发送到 #create 操作并尝试发出 POST 请求,而不是将表单发送到更新操作并制作当我点击提交时的 PUT 请求。

如何让 form_for 识别出我正在尝试更新现有对象并且需要 PUT 而不是 POST? 其他一切正常,并且我已经运行了所有迁移。我对 Rails 很陌生,我几乎整天都在尝试自己解决这个问题。请帮忙!

请注意,当我尝试将模型对象作为 @meal.log 而不是 :meal 传递时,Rails 不再能够识别 :calorie_estimate 或 :meal_description。将模型对象作为@meal.log 传递给我留下了无方法错误。

餐/edit.html.erb

<h3> EDIT MEAL </h3>
<%= form_for(:meal) do |f| %>
<div id="meal-form">
    <%= f.text_field :calorie_estimate, class: 'meal-form-fields', :placeholder => "Calorie Estimate" %>
    <%= f.text_field :meal_description, class: 'meal-form-fields', :placeholder => "Food Description" %>
  <div class="submit-form" style="width: 75px; height: 15px;">
    <%= f.submit 'UPDATE', :class => 'submit-form-text' %>
  </div>
</div>
<% end %>

meals_controller.rb

class MealsController < ApplicationController
  include MealsHelper

  def create
    @meal = Meal.new(meal_params)
    @meal.log_id = params[:log_id]
    @meal.save

    redirect_to log_path(@meal.log)
  end

  def edit
    @meal = Meal.find(params[:id])
  end

  def update
    @meal = Meal.find(params[:id])
    @meal.update(meal_params)
    redirect_to log_path(@log)
  end

  def meal_params
    params.require(:meal).permit(:calorie_estimate, :meal_description)
  end

end

可能的路线:

Prefix Verb   URI Pattern                            Controller#Action
         root GET    /                                      logs#index
    log_meals GET    /logs/:log_id/meals(.:format)          meals#index
              POST   /logs/:log_id/meals(.:format)          meals#create
 new_log_meal GET    /logs/:log_id/meals/new(.:format)      meals#new
edit_log_meal GET    /logs/:log_id/meals/:id/edit(.:format) meals#edit
     log_meal GET    /logs/:log_id/meals/:id(.:format)      meals#show
              PATCH  /logs/:log_id/meals/:id(.:format)      meals#update
              PUT    /logs/:log_id/meals/:id(.:format)      meals#update
              DELETE /logs/:log_id/meals/:id(.:format)      meals#destroy
         logs GET    /logs(.:format)                        logs#index
              POST   /logs(.:format)                        logs#create
      new_log GET    /logs/new(.:format)                    logs#new
     edit_log GET    /logs/:id/edit(.:format)               logs#edit
          log GET    /logs/:id(.:format)                    logs#show
              PATCH  /logs/:id(.:format)                    logs#update
              PUT    /logs/:id(.:format)                    logs#update
              DELETE /logs/:id(.:format)                    logs#destroy

routes.rb

Rails.application.routes.draw do

  root to: 'logs#index'

  resources :logs do
    resources :meals
  end
end

schema.rb

ActiveRecord::Schema.define(version: 20160128205351) do

  # These are extensions that must be enabled in order to support this database
  enable_extension "plpgsql"

  create_table "logs", force: :cascade do |t|
    t.string   "entry_date"
    t.integer  "calorie_goal"
    t.string   "notes"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

  create_table "meals", force: :cascade do |t|
    t.integer  "calorie_estimate"
    t.string   "meal_description"
    t.integer  "log_id"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

end

【问题讨论】:

    标签: ruby-on-rails ruby ruby-on-rails-4


    【解决方案1】:

    问题是您正在使用nested resources,因此您对将哪个@objects 传递给您的form_for 感到困惑。

    #app/views/meals/edit.html.erb
    <%= form_for [@log, @meal] do |f| %>
    

    正如您目前所拥有的那样,传递 :meal 是不明确的 - Rails 无法识别发送提交的路由/方法,因为它没有可用的数据。

    如果您想更新一个对象,您必须将适当的数据传递给表单,包括对象的id:

    <%= form_for :meal, url: { controller: "meals", action: "update", id: "5" }, method: :put do |f| %>
    

    比如 Rails 是object orientated,你最好将实际对象传递给你的form_for

    <%= form_for @meal ...
    

    --

    你的问题是你有一个嵌套资源:

    resources :logs do
      resources :meals #-> url.com/logs/:log_id/meals/:id
    end
    

    这意味着 you need to pass both the Log and Meal 值到您的 form

    #app/controllers/meals_controller.rb
    class MealsController < ApplicationController
      def edit
         @log  = Log.find params[:log_id]
         @meal = Meal.find params[:id]
      end
    
      def update
         @log  = Log.find params[:log_id]
         @meal = Meal.find params[:id]   
         @meal.update meal_params     
      end
    end
    
    #app/views/meals/edit.html.erb
    <%= form_for [@log, @meal] do |f| %>
    

    【讨论】:

    • 是的!非常感谢您的清晰解释。我在上面对饭菜控制器.rb 进行了更改并编辑了饭菜/edit.html.erb,以便我将日志和饭菜对象都传递给表单,就像这样` , but this left me with a No Method Error and undefined method meal_path'。对我有用的是做出你建议的对 foods_controller.rb 的更改并编辑饭菜/edit.html.erb 以呈现我用来创建新餐点的部分表单。再次感谢您的帮助!
    【解决方案2】:

    如果您的控制器正在创建一个名为@meal 的实例变量,您应该使用它而不是符号。所以写:

    form_for @meal do |f| 
    

    然后 rails 可以查询实例变量以查看它是 new_record?(在这种情况下它将 POST 数据)还是现有记录(其中它很可能是 PATCH)。

    要构建嵌套路由,您需要设置一个实例变量@log(我在您的代码中没有看到,但您可能已经这样做了),然后您可以编写:

    form_for [@log, @meal] do |f| 
    

    这将计算出正确的路径。

    【讨论】:

      【解决方案3】:

      你得到的错误:

        Routing Error
        No route matches [POST] "/logs/1/meals/13/edit
      

      表示您的表单执行的是 POST 而不是 PUT。

      要完成这项工作,只需在 form_for 声明中添加 method: :put

      <%= form_for(:meal, method: :put) do |f| %>
      

      【讨论】:

      • 这是一个奇怪的建议,它会发出一个 PUT,但它永远不会允许编辑现有记录。
      • OP 说他们想用 PUT 更新一条记录,这是如何实现的。
      • 所以,你清楚地说“更新”一条记录,你的答案是没有更新任何东西。只需使用 PUT 将新数据获取到服务器。
      • 检查:`PUT /logs/:log_id/meals/:id(.:format)饭菜#update` ...这怎么不更新餐食记录?
      • 因为您没有用之前包含的记录的数据填写表格?写form_for :mealform_for @meal的区别:第一个只是使用符号构建路径,第二个使用模型的名称状态(新/更新)和 模型的数据预填表格。
      猜你喜欢
      • 1970-01-01
      • 2012-04-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-01-24
      • 1970-01-01
      • 2014-08-14
      • 2019-11-09
      相关资源
      最近更新 更多