2012-10-24 55 views
1

我正在嘗試通過創建一個非常簡單的應用程序來創建一個網站,在這個應用程序中有人可以創建一個作者列表和一本書由作者編寫的關聯書籍。我希望這會很簡單,乾燥,但我一直有一個意想不到的麻煩。如何選擇適當的Association in Rails?

首先看我的模型,我已經建立了關聯,並且要求每個數據點(author.name,book.title和book.author)。我不想在attr_accessible列表中添加:author或author_id,因爲我想使用適當的Rails約定。

應用程序/模型/ author.rb:

class Author < ActiveRecord::Base 
    attr_accessible :name 
    validates_presence_of :name 
    has_many :books 
end 

應用程序/模型/ book.rb:

class Book < ActiveRecord::Base 
    attr_accessible :title 
    validates_presence_of :title 
    belongs_to :author 
    validates_associated :author 
end 

書籍控制器和視圖我想正是從腳手架和非常無趣。有趣的是書籍控制器。看着新的方法,我所做的只是添加一個收集器,該收集器使用ID將作者名稱數組傳遞給視圖。 (老實說,我認爲我寧願不通過ID在所有。)

應用程序/控制器/ books_controller.rb

# GET /books/new 
    # GET /books/new.json 
    def new 
    @book = Book.new 
    @authors = Author.all.collect {|a| [a.name, a.id]} 

    respond_to do |format| 
     format.html # new.html.erb 
     format.json { render json: @book } 
    end 
    end 

現在在各方面的意見,我使用了默認new.html.haml ,但對_form.html.haml進行了更改。我使用@authors中的值添加了一個選擇字段。

應用程序/視圖/書籍/ _form.html.haml

= form_for @book do |f| 
    - if @book.errors.any? 
    #error_explanation 
     %h2= "#{pluralize(@book.errors.count, "error")} prohibited this book from being saved:" 
     %ul 
     - @book.errors.full_messages.each do |msg| 
      %li= msg 

    .field 
    = f.label :name 
    = f.text_field :name 
    .field 
    = f.label :author 
    = f.select(:author, @authors, {:include_blank => ""}) 
.actions 
    = f.submit 'Save' 

最後,回到我的控制器的創建方法。我嘗試保存基本參數並從所選作者創建作者關聯。

應用程序/控制器/ books_controller.rb

# POST /books 
    # POST /books.json 
    def create 
    @book = Book.new(params[:book]) 
    @book.author = Author.find_by_id(params[:author]) 

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

當我點擊「保存」我得到以下錯誤:

Can't mass-assign protected attributes: author 

我明白,這是因爲我選擇的值放入params [:book]而不是params [:author]。所以我有兩個問題。

1)我該如何解決我的select語句讓它以params [:author]而不是params [:book]發送?

2)有沒有更好的方法來做到這一點,完全隱藏ID協會?

回答

0

我想我已經想通大多出來,並作爲我懷疑,我沒有根本不需要改變我的模型代碼。

我改變@authors我的控制器,新的方法定義只返回作者姓名改爲:

@authors = Author.pluck(:name) 

這完成了我隱藏了ID的目標,雖然它可能是一個稍微慢的時候,我需要通過搜索名稱,而不是控制器創建方法的id(如下)。

接下來,我修復了我的觀點,以設置params [:author]而不是params [:book] [:author]。

= label :author, :name, 'Author' 
= select_tag(:author, options_for_select(@authors), {:include_blank => ""}) 

最後,我換了新的方法創作的@book的:

@book = Author.find_by_name(params[:author]).books.create(params[:book]) 

我與這個相當高興。我唯一不喜歡的是標籤創建了一個「author_name」標籤,而不是簡單的「author」。

+0

糟糕,我想我可以使用label_tag:作者。 – Geoff

+0

我最終將控制器更改爲'@book = Book.new(params [:book])'和'@book.author = Author.find_by_name(params [:author])''。這有點容易閱讀,不會導致大量屬性警告,並確保我有一個'@ book',如果因爲錯誤需要重新渲染,可能會出現錯誤標記。 – Geoff

0

「不能大規模指派保護屬性:作者」指的是在模型的屬性沒有被列爲attr_accessible

+0

是的,那是故意的。但我的問題的關鍵是如何在不改變模型的情況下做到這一點「工作」? – Geoff