Rails: No route matches … missing required keys: [

2019-05-20 08:13发布

问题:

Full error:

No route matches {:id=>#<Reminder id: nil, medication: nil, time: nil, created_at: nil, updated_at: nil, user_id: 1>} missing required keys: [:id]

Here is the code from index.html.erb that is causing the error:

<tbody>
    <% @reminders.each do |reminder| %>
      <tr <%= dom_id(reminder) %>>
        <td><%= reminder.medication %></td>
        <td><%= reminder.time %></td>
        <td>
          <%= link_to "Edit", edit_reminder_path(reminder) %>
          or
          <%= link_to 'Remove', reminder, method: :delete, data: { confirm: 'Are you sure?' } %>
      </td>
      </tr>
    <% end %>
  </tbody>

Models:

class Reminder < ActiveRecord::Base
    validates :medication, presence: true
    belongs_to :user
end
class User < ActiveRecord::Base
  has_many :reminders
end

Actions:

  def index
    @reminders = current_user.reminders
    @reminder = current_user.reminders.new 
  end
  def edit
  end

Routes:

Medy::Application.routes.draw do
  devise_for :users
  resources :reminders
  root 'reminders#index'
end

Do I need to add something to the edit action to make this work?

The error started happening after I changed @reminders = Reminders.all to @reminders = current_user.reminders in the index action.

回答1:

The reason for this is that you are passing an unsaved instanced of 'reminder' into the edit_reminder_path. When you pass an instance as a parameter to a route the 'to_param' method is called on that instance. By default the 'to_param' method returns the 'id' of the instance.

In your case you have:

def index
  @reminders = current_user.reminders
  @reminder = current_user.reminders.new
end

Because you have scoped the @reminder to the current user. That instance is added to the collection of reminders for that user, and so is also included in @reminders. This means that when you are rendering out the @reminders on the index page, It also tries to render out the unsaved @reminder which has not yet got an 'id' set.

A better solution would be to change you index action to:

def index
  @reminders = current_user.reminders
  @reminder = Reminder.new
end

And then at the point that you save the reminder, most likely in a 'create' action, you would scope to the current_user:

def create
  @reminder = current_user.reminders.new(reminder_params)
  if @reminder.save .....
end


回答2:

The error is in your index action:

def index
  @reminders = current_user.reminders
  @reminder = current_user.reminders.new # <== HERE
end

You're adding a new unpersisted instance to the current_user.reminders collection, which is what this error is telling you when you iterate that collection and try to link_to that one: No route matches {:id=>#<Reminder id: nil



回答3:

Bug relatado em / Bug reported in https://github.com/rails/rails/issues/12178

class ApplicationController < ActionController::Base
  protect_from_forgery
  before_action :set_locale

  def default_url_options(options={})
    { locale: I18n.locale }
  end

  private
    def set_locale
      I18n.locale = params[:locale] || I18n.default_locale
    end
end

ou dessa maneira resolve o bug / or that way solves the bug

# app/controllers/application_controller.rb
def default_url_options(options = {})
  { locale: I18n.locale }.merge options
end

Ficaria assim /would look like this

# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
      protect_from_forgery
      before_action :set_locale

     def default_url_options(options = {})
      { locale: I18n.locale }.merge options
     end

      private
        def set_locale
          I18n.locale = params[:locale] || I18n.default_locale
        end
end