2013-06-03 2 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 

Проблема

На create или update действий, если категория еще не существует, пе w будет создан в отдельной транзакции. Так что, если в произошла ошибка, в любом случае можно создать новую категорию.

Журнал создания действия/обновления (обрезается для краткости):

(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

Взгляните на Railscast на [Токен] (http://railscasts.com/episodes/258-token-fields?view=asciicast). Это требует дополнительных шагов, но Райан отлично справился с этим. – Firyn

ответ

2
  1. Я бы не беспокоился о том, что 1 строка кода DRY или нет.
  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. В противном случае он назначит новую категорию через вложенные атрибуты, которые обеспечат сохранение категории, если нет других ошибок проверки для статьи и не будут сохраняться, если они есть.

Смежные вопросы