NoMethodError:未定义的方法`id' for nil:尝试设置link_to时的NilClass

时间:2014-08-22 05:45:24

标签: ruby-on-rails twitter-bootstrap routes

我面临以下问题:

我有一个模型“餐厅”,我想在导航栏中创建一个“编辑餐厅”链接。我尝试在导航栏中插入这个li:

<li><%= link_to "Edit Listing", edit_restaurant_path(@restaurant.id) %></li>

但是通过这样做我在完全相同的行上得到了这个错误:

NoMethodError : undefined method `id' for nil:NilClass

我猜:id没有正确传递

以下是我的其他相应文件:

restaurant_controler.rb

      class RestaurantsController < ApplicationController
    before_action :set_restaurant, only: [:show, :edit, :update, :destroy]
    before_action :authenticate_user!, except: [:search, :index, :show]
    before_action :check_user, except: [:search, :index, :show]


    def search
      if params[:search].present?
         @restaurants = Restaurant.search(params[:search])
      else
         @restaurants = Restaurant.all
      end
    end

    # GET /restaurants
    # GET /restaurants.json
    def index
      @restaurants = Restaurant.all
    end

    # GET /restaurants/1
    # GET /restaurants/1.json
    def show
      @reviews = Review.where(restaurant_id: @restaurant.id).order("created_at DESC")
      if @reviews.blank?
         @avg_rating = 0
      else
         @avg_rating = @reviews.average(:rating).round(2)
      end
    end

    # GET /restaurants/new
    def new
      @restaurant = Restaurant.new
    end

    # GET /restaurants/1/edit
    def edit
      @restaurant = Restaurant.find(params[:id])
    end

    # POST /restaurants
    # POST /restaurants.json
    def create
      @restaurant = Restaurant.new(restaurant_params)

      respond_to do |format|
        if @restaurant.save
          format.html { redirect_to @restaurant, notice: 'Restaurant was successfully created.' }
          format.json { render action: 'show', status: :created, location: @restaurant }
        else
          format.html { render action: 'new' }
          format.json { render json: @restaurant.errors, status: :unprocessable_entity }
        end
      end
    end

    # PATCH/PUT /restaurants/1
    # PATCH/PUT /restaurants/1.json
    def update
      respond_to do |format|
        if @restaurant.update(restaurant_params)
          format.html { redirect_to @restaurant, notice: 'Restaurant was successfully updated.' }
          format.json { head :no_content }
        else
          format.html { render action: 'edit' }
          format.json { render json: @restaurant.errors, status: :unprocessable_entity }
        end
      end
    end

    # DELETE /restaurants/1
    # DELETE /restaurants/1.json
    def destroy
      @restaurant.destroy
      respond_to do |format|
        format.html { redirect_to restaurants_url }
        format.json { head :no_content }
      end
    end

    private
      # Use callbacks to share common setup or constraints between actions.
      def set_restaurant
        @restaurant = Restaurant.find(params[:id])
      end

      def check_user
        unless current_user.admin?
          redirect_to root_url, 
          alert: "Sorry, only admins can do that!"
        end
      end

      # Never trust parameters from the scary internet, only allow the white list through.
      def restaurant_params
        params.require(:restaurant).permit(:name, :adress, :phone, :website, :image, :description)
      end
  end

导航栏:

   <nav class="navbar navbar-default" role="navigation">
    <div class="container-fluid">
      <!-- Brand and toggle get grouped for better mobile display -->
      <div class="navbar-header">
        <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
          <span class="sr-only">Toggle navigation</span>
          <span class="icon-bar"></span>
          <span class="icon-bar"></span>
          <span class="icon-bar"></span>
        </button>
        <%= link_to "ListingsDemo", root_path, class: "navbar-brand" %>
      </div>

      <!-- Collect the nav links, forms, and other content for toggling -->
      <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
        <ul class="nav navbar-nav">
          <li><%= link_to "About", pages_about_path %></li>
          <li><%= link_to "Contact", pages_contact_path %></li>
        </ul>
        <p>
        <%= form_tag search_restaurants_path, method: :get, class: "navbar-form navbar-left" do %>
              <%= text_field_tag :search, params[:search], class: "form-control" %>
              <%= submit_tag "Search", name: nil, class: "btn btn-default" %>
          <% end %>
          </p>
        <ul class="nav navbar-nav navbar-right">
          <% if user_signed_in? %>
            <li><%= link_to "Edit Profile", edit_user_registration_path %></li>
           <li><%= link_to "Edit Listing", edit_restaurant_path(@restaurant.id) %></li>
            <li><%= link_to "Sign Out", destroy_user_session_path, method: :delete %></li>
          <% else %>
             <li><%= link_to "Sign In", new_user_session_path %></li>
             <li><%= link_to "Sign Up", new_user_registration_path %></li>
          <% end %>
        </ul>
      </div><!-- /.navbar-collapse -->
    </div><!-- /.container-fluid -->
  </nav>

的routes.rb

      Yelpdemo::Application.routes.draw do


    resources :messages

    devise_for :admin_users, ActiveAdmin::Devise.config
    ActiveAdmin.routes(self)

    devise_for :users
    resources :restauarants do
      collection do
        get 'search'
      end
      resources :reviews, except: [:show, :index]
    end


    get "pages/about"
    get 'pages/contact', to: redirect('/messages/new')
    root 'restaurants#index'

  end

Restaurans / index.html.erb

  <table class="table table-responsive table-hover table-condensed">
    <thead>
      <tr>
        <th>Name</th>
        <th>Adress</th>
        <th>Phone</th>
        <th>Website</th>
        <% if user_signed_in? && current_user.admin? %>
           <th colspan="2"></th>
        <% end %>
        <th></th>
        <th></th>
      </tr>
    </thead>

    <tbody>
      <% @restaurants.each do |restaurant| %>
        <tr>
          <td><%= link_to restaurant.name, restaurant %></td>
          <td><%= restaurant.adress %></td>
          <td><%= restaurant.phone %></td>
          <td><%= link_to restaurant.website, restaurant.website %></td>
          <% if user_signed_in? && current_user.admin? %>
            <td><%= link_to 'Edit', edit_restaurant_path(restaurant), class: 'btn btn-link' %></td>
            <td><%= link_to 'Destroy', restaurant, method: :delete, data: { confirm: 'Are you sure?' }, class: 'btn btn-link' %></td>
          <% end %>
        </tr>
      <% end %>
    </tbody>
  </table>

  <br>

  <% if user_signed_in? && current_user.admin? %>

    <%= link_to 'New Restaurant', new_restaurant_path, class: 'btn btn-link' %>

  <% end %>

  <%= image_tag "https://s3-eu-west-1.amazonaws.com/listingsdemo/happy_footer.png" %>

3 个答案:

答案 0 :(得分:1)

<强>餐厅

您遇到的问题不是id方法没有被传递 - 而是您的@restaurant对象尚未被声明。

当我查看您的代码时,我注意到您提到错误在您的导航栏中。这引起了我的担忧,因为您视图的 navbar 元素表明它会持续存在多个视图

如果是这种情况,您需要确保为每个操作声明了@restaurant变量:

#app/controllers/application_controller.rb
Class ApplicationController < ActionController::Base
   before_action :set_restaurant

   private

   def set_restaurant
      @restaurant = Restaurant.find 1 #-> you need to identify the restaurant
   end
end

答案 1 :(得分:0)

您始终会在indexsearch操作中收到此错误消息。因为你在这个方法中得到餐馆对象的列表而不是餐馆的单个对象。在索引操作的情况下,您将获得@restaurant nil。在索引和搜索操作的情况下,您必须从导航栏中删除此操作/链接。希望这能帮到你!

答案 2 :(得分:0)

您在哪个视图/路线中点击导航栏链接?没有信息,但看起来你的@restaurant变量是零,所以你实际上没有从控制器传递给视图。

另一件事是 - 一般来说,您可以将erb中的路径称为

edit_restaurant_path(@restaurant)

(不需要'.id')