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 %>
...然后在点击索引后检查您的日志。
我在 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 %>
...然后在点击索引后检查您的日志。