在一个事务中分配或创建关联模型

时间:2013-06-03 23:17:45

标签: ruby-on-rails ruby-on-rails-3 activerecord

我有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

问题

createupdate操作中,如果该类别尚不存在,则将在单独的事务中创建新类别。因此,如果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原则,我宁愿找到另一种方式。

2 个答案:

答案 0 :(得分:2)

  1. 我不会关注1行代码是否干燥。
  2. 你可以做类似以下的事情
  3. 请注意,我不喜欢这个,但这是可行的

    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。否则,它将通过嵌套属性分配一个新类别,如果文章没有其他验证错误,则确保该类别保持持久化,如果存在,则不会保留。