我有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原则,我宁愿找到另一种方法。
- 我不会关心一行代码是否为DRY。
- 你可以这样做
请注意,我不喜欢这样,但这是可行的
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
老问题,但以防有人正在寻找类似问题的解决方案:
我认为你应该能够处理它,而不干扰事务,并使用嵌套属性如下:
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。否则,它将通过嵌套属性分配一个新类别,该属性将确保在没有其他验证错误的情况下持久化该类别,如果有,则不持久化该类别。