手动更改路由时未将 ID 参数发送到控制器

ID param not being sent to Controller when manually changing routes

我查看了 Whosebug 上提供的其他答案,其中 none 回答了我的问题。这是我的代码发生的情况。

错误

undefined method `update' for nil:NilClass

问题: 看起来参数 id 没有从表单发送到控制器,因为它们在使用 byebug 的控制台中显示为 nil。

控制台读数:

(byebug) params[:id]
nil
(byebug) @support
nil
(byebug) params[:title]
nil
(byebug) params[:support]
<ActionController::Parameters {"title"=>"Test", "subtitle"=>"testing", 
"website"=>"www.test.com", "type_of_support"=>"", "description"=>""}
permitted: false>
(byebug) params[:support][:id]
nil
(byebug) params[:support][:title]
"Test"

我认为问题不在于表单,因为它与用于 new/create 操作的部分表单相同,然后将参数发送到控制器并创建对象(尽管在那如果没有 id,因为它是在创建对象时生成的,而不是从表单传递的)。

你可以在我下面的代码中看到 PATCH 的路由只是 'support' 而没有 :id 参数。如果我尝试将其添加到路由中,则会收到一条错误消息,指出没有路由匹配 'support/'。所以,我必须去掉路由中的 :id 参数才能将信息传递给控制器​​。

我在这里不知所措。如何将 :id 传递给控制器​​? rails 是怎么做到的?在我手动更改路线之前,来自 resources :supports 的自动路线包含 PATCH 路线的 :id 参数并且它有效。我做错了什么,它不允许我将其添加到路线中?

代码:

config/routes.rb

get    'support',          as: 'supports',     to: 'supports#index'
post   'support',                              to: 'supports#create'
get    'support/new',      as: 'new_support',  to: 'supports#new'
get    'support/:id/edit', as: 'edit_support', to: 'supports#edit'
get    'support/:title',   as: 'support_page', to: 'supports#show'
patch  'support/',                             to: 'supports#update'
put    'support/:id',                          to: 'supports#update'
delete 'supports/:id',                         to: 'supports#destroy'

佣金路线的结果:

supports     GET    /support(.:format)                supports#index
support      POST   /support(.:format)                supports#create
new_support  GET    /support/new(.:format)            supports#new
edit_support GET    /support/:id/edit(.:format)       supports#edit
support_page GET    /support/:title(.:format)         supports#show
             PATCH  /support(.:format)                supports#update
             PUT    /support/:id(.:format)            supports#update
             DELETE /supports/:id(.:format)           supports#destroy

app/controllers/supports_controllers.rb

class SupportsController < ApplicationController
  before_action :set_support_by_title, only: [:show]
  before_action :set_support_by_id,    only: [:edit, :update, :destroy]

  def index
    @supports = Support.all
  end

  def show
  end

  def new
    @support = Support.new
  end

  def edit
  end

  def create
    @support = Support.new(support_params)

    respond_to do |format|
      if @support.save
        format.html { redirect_to @support, 
                      notice: 'Support was successfully created.' }
      else
        format.html { render :new }
      end
    end
  end

  def update
    # byebug
    respond_to do |format|
      if @support.update(support_params)
        format.html { redirect_to @support, 
                      notice: 'Support was successfully updated.' }
      else
        format.html { render :edit }
      end
    end
  end

  def destroy
    @support.destroy
    respond_to do |format|
      format.html { redirect_to supports_url, 
                    notice: 'Support was successfully destroyed.' }
    end
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_support_by_title
      @support = Support.find_by(title: params[:title])
      # byebug
    end

    def set_support_by_id
      @support = Support.find(params[:id])
      # byebug
    end

    # Never trust parameters from the scary internet, 
    # only allow the white list through.

    def support_params
      params.require(:support).permit(:title, 
                                      :subtitle, 
                                      :website, 
                                      :type_of_support, 
                                      :description)
    end
end

app/views/supports/edit.html.erb

<h1>Editing Support</h1>

<%= render 'form', support: @support %>

<%= link_to 'Show', support_page_path(@support.title) %> |
<%= link_to 'Back', supports_path %>

app/views/supports/_form.html.erb

<%= form_with(model: support, local: true) do |form| %>
  <% if support.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(support.errors.count, "error") %> 
          prohibited this support from being saved:
      </h2>

      <ul>
      <% support.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    Title: 
    <%= form.text_field :title, id: :support_title %>
  </div>

  <div class="field">
    Subtitle: 
    <%= form.text_field :subtitle, id: :support_subtitle %>
  </div>

  <div class="field">
    Website: 
    <%= form.text_field :website, id: :support_website %>
  </div>

  <div class="field">
    Type of Support: 
    <%= form.text_field :type_of_support, id: :support_type %>
  </div>

  <div class="field">
    Description: 
    <%= form.text_area :description, id: :support_description %>
  </div>

  <div class="actions">
    <%= form.submit %>
  </div>
<% end %>

在写这道题的时候,我想到了一个东西,试了一下。它奏效了。我没有自己重写所有路由,而是只写了 2 条并使用 resources :supports, except: [:index, :show] 让 rails 生成其他路由。这解决了我的问题。

说明

我知道幕后发生了一些我不明白的事情。在我开始更改路线之前,整个过程运行良好。所以,里面有些东西是不正确的。 (我仍然不知道它是什么以及如何更改它。)

我真正想要更改的唯一两条路线是用户看到的两条路线。我不关心路由在管理后端中的外观。所以,这意味着我只需要更改 index 和 show 的路由,使其对 SEO 友好并且在浏览器中看起来更好。所以,为了做到这一点,我写了这样的路线:

config/routes.rb

resources :supports, except: [:index, :show]

get 'support', as: 'support_index', to: 'supports#index'
get 'support/:title', as: 'support_page', to: 'supports#show'

然后为我创建了所有新的、创建、编辑、更新、销毁路线。这样做之后,这就是我的路线现在的样子:

supports      POST   /supports(.:format)               supports#create
new_support   GET    /supports/new(.:format)           supports#new
edit_support  GET    /supports/:id/edit(.:format)      supports#edit
support       PATCH  /supports/:id(.:format)           supports#update
              PUT    /supports/:id(.:format)           supports#update
              DELETE /supports/:id(.:format)           supports#destroy
support_index GET    /support(.:format)                supports#index
support_page  GET    /support/:title(.:format)         supports#show

如您所见,PATCH 路由现在正在获取参数 :id 以便能够更新记录。

现在,我只需要像这样在创建、更新和销毁后更改控制器中的一些重定向:

def create
  @support = Support.new(support_params)

  respond_to do |format|
    if @support.save
      format.html { redirect_to support_page_path(title: @support.title), 
                    notice: 'Support was successfully created.' }
    else
      format.html { render :new }
    end
  end
end

def update
  respond_to do |format|
    if @support.update(support_params)

      format.html { redirect_to support_page_path(title: @support.title), 
                    notice: 'Support was successfully updated.' }
    else
      format.html { render :edit }
    end
  end
end

def destroy
  @support.destroy
  respond_to do |format|
    format.html { redirect_to support_index_path, 
                  notice: 'Support was successfully deleted.' }
  end
end

这些 redirect_to 语句现在匹配我为索引和显示生成的路由。

现在一切正常。

所以,问题解决了,虽然我仍然不知道我之前做错了什么。任何可以发出的光将不胜感激。