我有articles
,每篇文章都有_ 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
问题
在create
或update
操作中,如果该类别尚不存在,则将在单独的事务中创建新类别。因此,如果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
但这意味着我必须在两种方法中编写相同的代码:create
和update
。由于它违反了DRY原则,我宁愿找到另一种方式。
答案 0 :(得分: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
答案 1 :(得分: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。否则,它将通过嵌套属性分配一个新类别,如果文章没有其他验证错误,则确保该类别保持持久化,如果存在,则不会保留。