Rails 关联错误

Rails Association Bug

我在 SitesController 中收到 NoMethodError#index undefined method `name' for nil:NilClass 在我的 Salesman 索引视图中和找不到罪魁祸首。

我有一个简单的 rails 应用,包含以下 table:客户、销售员和发票。

在客户的索引视图中 table 我有以下调用:

<% @customers.each do |customer| %> <%= customer.name %></td> <%= customer.address %> <%= customer.salesman.name %> <% end %>

这是导致上面列出的未定义方法“name”的调用。我确保销售员 table 在客户 table 中有一个 salesman_id 外键。另外,我在模型中做了相应的关联:

class Customer < ActiveRecord::Base belongs_to :salesman has_many :invoices end

class Salesman < ActiveRecord::Base has_many :customers end

我在控制台中测试了 customer.salesman.name 调用,它运行完美。客户的展示视图有一个像这样的精确调用,它也有效。我可以让客户索引通过的唯一方法是将 customer.salesman.name 替换为 customer.salesman_id,只返回 id。

感谢您抽出宝贵时间。

***schema.rb*** 

ActiveRecord::Schema.define(version: 20150325172212) do
create_table "customers", force: :cascade do |t|
    t.string   "name"
    t.string   "address"
    t.datetime "created_at",               null: false
    t.datetime "updated_at",               null: false
    t.integer  "salesman_id"
  end
create_table "invoices", force: :cascade do |t|
    t.datetime "date"
    t.string   "fid"
    t.integer  "salesman_id"
    t.datetime "created_at",      null: false
    t.datetime "updated_at",      null: false
    t.string   "address"
  end
create_table "salesmen", force: :cascade do |t|
    t.datetime "name"
    t.string   "address""
    t.datetime "created_at",      null: false
    t.datetime "updated_at",      null: false
  end 

***controllers***

**customer**

Class CustomersController < ApplicationController
  before_action :set_customer, only: [:show, :edit, :update, :destroy]
  # GET /customers
  # GET /customers.json
  def index
    @customers = Customer.all
  end
  # GET /customers/1
  # GET /customers/1.json
  def show
  end
  # GET /customers/new
  def new
    @customer = Customer.new
  end
  # GET /customers/1/edit
  def edit
  end
  # POST /customers
  # POST /customers.json
  def create
    @customer = Customer.new(customer_params)
    respond_to do |format|
      if @customer.save
        format.html { redirect_to @customer, notice: 'Customer was successfully created.' }
        format.json { render :show, status: :created, location: @customer }
      else
        format.html { render :new }
        format.json { render json: @customer.errors, status: :unprocessable_entity }
      end
    end
  end
  # PATCH/PUT /customers/1
  # PATCH/PUT /customers/1.json
  def update
    respond_to do |format|
      if @customer.update(customer_params)
        format.html { redirect_to @customer, notice: 'Customer was successfully updated.' }
        format.json { render :show, status: :ok, location: @customer }
      else
        format.html { render :edit }
        format.json { render json: @customer.errors, status: :unprocessable_entity }
      end
    end
  end
  # DELETE /customers/1
  # DELETE /customers/1.json
  def destroy
    @customer.destroy
    respond_to do |format|
      format.html { redirect_to customers_url, notice: 'Customer was successfully destroyed.' }
      format.json { head :no_content }
    end
  end
  private
    # Use callbacks to share common setup or constraints between actions.
    def set_cliente
      @cliente = Cliente.find(params[:id])
    end
    # Never trust parameters from the scary internet, only allow the white list through.
    def customer_params
      params.require(:customer).permit(:name, :address, :salesman_id)
    end
end

***salesman***

class SalesmenController < ApplicationController
  before_action :set_salesman, only: [:show, :edit, :update, :destroy]
  # GET /salesmans
  # GET /salesmans.json
  def index
    @salesmen = Salesman.all
  end
  # GET /salesmans/1
  # GET /salesmans/1.json
  def show
  end
  # GET /salesmans/new
  def new
    @salesman = Salesman.new
  end
  # GET /salesmans/1/edit
  def edit
  end
  # POST /salesmans
  # POST /salesmans.json
  def create
    @salesman = Salesman.new(salesman_params)
    respond_to do |format|
      if @salesman.save
        format.html { redirect_to @salesman, notice: 'Salesman was successfully created.' }
        format.json { render :show, status: :created, location: @salesman }
      else
        format.html { render :new }
        format.json { render json: @salesman.errors, status: :unprocessable_entity }
      end
    end
  end
  # PATCH/PUT /salesmans/1
  # PATCH/PUT /salesmans/1.json
  def update
    respond_to do |format|
      if @salesman.update(salesman_params)
        format.html { redirect_to @salesman, notice: 'Salesman was successfully updated.' }
        format.json { render :show, status: :ok, location: @salesman }
      else
        format.html { render :edit }
        format.json { render json: @salesman.errors, status: :unprocessable_entity }
      end
    end
  end
  # DELETE /salesmans/1
  # DELETE /salesmans/1.json
  def destroy
    @salesman.destroy
    respond_to do |format|
      format.html { redirect_to salesmans_url, notice: 'Salesman was successfully destroyed.' }
      format.json { head :no_content }
    end
  end
  private
    # Use callbacks to share common setup or constraints between actions.
    def set_salesman
      @salesman = Salesman.find(params[:id])
    end
    # Never trust parameters from the scary internet, only allow the white list through.
    def salesman_params
      params.require(:salesman).permit(:name, :address)
    end

**invoice**

class InvoicesController < ApplicationController
  before_action :set_invoice, only: [:show, :edit, :update, :destroy]
  # GET /invoices
  # GET /invoices.json
  def index
    @invoices = Invoice.all
  end
  # GET /invoices/1
  # GET /invoices/1.json
  def show
    @invoice = Invoice.find(params[:id])
    @ordenes = @invoice.ordenes
  end
  # GET /invoices/new
  def new
    @invoice = Invoice.new
  end
  # GET /invoices/1/edit
  def edit
  end
  # POST /invoices
  # POST /invoices.json
  def create
    @invoice = Invoice.new(invoice_params)
    respond_to do |format|
      if @invoice.save
        format.html { redirect_to @invoice, notice: 'Invoice was successfully created.' }
        format.json { render :show, status: :created, location: @invoice }
      else
        format.html { render :new }
        format.json { render json: @invoice.errors, status: :unprocessable_entity }
      end
    end
  end
  # PATCH/PUT /invoices/1
  # PATCH/PUT /invoices/1.json
  def update
    respond_to do |format|
      if @invoice.update(invoice_params)
        format.html { redirect_to @invoice, notice: 'Invoice was successfully updated.' }
        format.json { render :show, status: :ok, location: @invoice }
      else
        format.html { render :edit }
        format.json { render json: @invoice.errors, status: :unprocessable_entity }
      end
    end
  end
  # DELETE /invoices/1
  # DELETE /invoices/1.json
  def destroy
    @invoice.destroy
    respond_to do |format|
      format.html { redirect_to invoices_url, notice: 'Invoice was successfully destroyed.' }
      format.json { head :no_content }
    end
  end
  private
    # Use callbacks to share common setup or constraints between actions.
    def set_invoice
      @invoice = Invoice.find(params[:id])
    end
    # Never trust parameters from the scary internet, only allow the white list through.
    def invoice_params
      params.require(:invoice).permit(:date, :fid, :name, :address, :salesman_id)
    end
end

-davefogo

将以下内容添加到您的视图中,而不是您当前拥有的内容<%= customer.salesman.name if customer.salesman %>

这将确保您不会在客户没有推销员时致电 name

试试这个:

<% @customers.each do |customer| %> 
  <%= customer.name %> 
  <%= customer.address %> 
  <%= customer.salesman.try(:name) %> 
<% end %>

这样做:

<% @customers.each do |customer| %>
  <% Rails.logger.debug "\n\n#{@customers} has a nil customer in it\n\n" if customer.nil? %>
  <% Rails.logger.debug "\n\nCustomer #{customer.id} has no salesman for [#{customer.salesman_id}]\n\n" if customer.salesman.nil? %>
  <%= customer.name %>
  <%= customer.address %>
  <%= customer.salesman.name %>
<% end %>

...然后在点击索引后检查您的日志。