2014-04-09 52 views
0

我使用rails 4並定義了模型書和isbn之間的一對一關係,但無法訪問嵌套屬性。下面是代碼Rails 4有一個關係嵌套屬性throw undefined方法異常

book.rb

class Book < ActiveRecord::Base 
    has_one :isbn 
    accepts_nested_attributes_for :isbn 
end 

isbn.rb

class Isbn < ActiveRecord::Base 
    belongs_to :book 
end 

books_controller.rb

class BooksController < ApplicationController 
    before_action :set_book, only: [:show, :edit, :update, :destroy] 

    # GET /books 
    # GET /books.json 
    def index 
    @books = Book.find_by_sql(["select * from books where price > ?", 20]) 
    end 

    # GET /books/1 
    # GET /books/1.json 
    def show 
    end 

    # GET /books/new 
    def new 
    @book = Book.new 
    end 

    # GET /books/1/edit 
    def edit 
    end 

    # POST /books 
    # POST /books.json 
    def create 
    @book = Book.new(book_params) 

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

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

    # DELETE /books/1 
    # DELETE /books/1.json 
    def destroy 
    @book.destroy 
    respond_to do |format| 
     format.html { redirect_to books_url } 
     format.json { head :no_content } 
    end 
    end 

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

    # Never trust parameters from the scary internet, only allow the white list through. 
    def book_params 
     params.require(:book).permit(:name, :author, :price, isbn_attributes: [:number]) 
    end 
end 

書\ index.html.erb

<table> 
    <tr> 
    <th>#</th> 
    <th>name</th> 
    <th>author</th> 
    <th>price</th> 
    <th>ISBN</th> 
    <th colspan="3">operation</th> 
    </tr> 
<% @books.each do |book| %> 
    <tr> 
    <td><%= book.id %></td> 
    <td><%= book.name %></td> 
    <td><%= book.author %></td> 
    <td><%= book.price %></td> 
    <td><%= book.isbn.number %></td> 
    <td><%= link_to 'detail', book %></td> 
    <td><%= link_to 'edit', edit_book_path(book) %></td> 
    <td><%= link_to 'del', book, confirm: 'Are you sure?', method: :delete %></td> 
    </tr> 
<% end %> 
</table> 

「<%= book.isbn.number%>」例外:對於零未定義的方法`號碼」:NilClass。

如何解決此問題?謝謝!

回答

1

發生這種情況,因爲該特定book沒有isbn存在,來處理這個添加條件<%= book.isbn.number %>

<td><%= book.isbn.number unless book.isbn.blank? %></td> 
2

最簡單的方法是檢查isbn.nil?在訪問其屬性之前。

但有更好的方法來做到這一點。

  1. 使用委託,這樣不違反Law of Demeter

    class Book < ActiveRecord::Base 
        has_one :isbn 
        accepts_nested_attributes_for :isbn 
        delegate :number, to: :isbn, allow_nil: true, prefix: true 
    end 
    
    # then in your view 
    book.isbn_number 
    
  2. 使用的裝飾,draper是最流行的寶石。

    class BookDecorator < Draper::Decorator 
        delegate_all 
    
        def isbn_number 
        # view can be more complex 
        isbn.number unless isbn.nil? 
        end 
    end 
    
    # don't forget to wrap your book instance in controller 
    

裝飾器用於累積視圖邏輯。當你需要比簡單檢查存在更復雜的東西時使用它們。另外,不要讓你的模型太多,而是使用很多委託者/方法,而是使用裝飾器。

在極少數情況下,您可能需要您的關聯始終在父母時建立/創建。你可以do it in your controller。此外,您可以使用ActiveRecord回調函數,並在父初始化上構建關聯,因爲在測試中主要存在一些副作用,因此不建議這樣做。

+0

+1此邏輯 – RSB