2013-06-03 49 views
2

我有articles,每篇文章has_many categories在一筆交易中指定或創建關聯模型

當用戶創建或更新文章時,他或她可以填寫類別名稱,如果此類別不存在,則需要創建一個新類別。

模式

class Article < ActiveRecord::Base 
    attr_accessible :category_id, :content, :title, :category_name 

    belongs_to :category 

    def category_name 
    category.try(:name) 
    end 

    def category_name=(name) 
    self.category = Category.find_or_create_by_name(name) if name.present? 
    end 
end 

class Category < ActiveRecord::Base 
    attr_accessible :name 

    has_many :articles 
end 

控制器

class ArticlesController < ApplicationController 
    load_and_authorize_resource 
    respond_to :json 

    def create 
    @article = Article.create(params[:article]) 
    respond_with(@article) 
    end 

    def update 
    @article.update_attributes(params[:article]) 
    @article.save 
    respond_with(@article) 
    end 

    ... 
end 

問題

createupdate行動,如果類別不存在,那麼NE w將在單獨的交易中創建。因此,如果在article中出現錯誤,則無論如何都可以創建新類別。

創建/更新操作的日誌(修剪爲了簡潔):

(0.0ms) begin transaction 
    SQL (0.3ms) INSERT INTO "categories" .... 
    (35.1ms) commit transaction 

    (0.0ms) begin transaction 
    SQL (0.5ms) INSERT INTO "articles" ... 
    (32.2ms) commit transaction 

我希望得到一些建議/解決方案如何以優雅的方式解決這個問題。

我大概可以寫在我的控制器

ActiveRecord::Base.transaction do 
    @article = Article.create(params[:article]) 
    respond_with(@article) 
end 

但它意味着我不得不寫相同的代碼在這兩種方法:createupdate。既然它違反了DRY原則,我寧願找到另一種方式。

+0

看看[Token]上的Railscast(http://railscasts.com/episodes/258-token-fields?view=asciicast)。它需要一些額外的步驟,但瑞安很好地解釋它。 – Firyn

回答

2
  1. 我不會關心一行代碼是幹還是不幹。
  2. 你可以做類似下面

請注意,我不喜歡這個,但它是可行的

def update 
    article = Article.find(params[:id]) 
    article.attributes = params[:article) 
    respond_with persist(article) 
end 

def create 
    article = Article.new(params[:article]) 
    respond_with persist(article) 
end 

private 

def persist(article) 
    ActiveRecord::Base.transaction do 
    return article.save 
    end 
end 
0

老問題,但以防萬一,如果有人正在尋找的解決方案類似的問題:

我想你應該能夠處理它,而不與交易搞亂,並通過使用嵌套的屬性如下:

class Article < ActiveRecord::Base 
    # ... 

    belongs_to :category 

    accepts_nested_attributes_for :category 

    def category_name=(name) 
    category = Category.find_by(name: name) 
    if category 
     self.category_id = category.id 
    else 
     self.category_attributes = {name: name} 
    end 
    end 
end 

因此,如果存在具有該名稱的類別,它將只分配category_id。否則,它將通過嵌套屬性分配一個新類別,這將確保類別在文章沒有其他驗證錯誤的情況下持續存在,並且如果存在則不會持續存在。