ZelluX
ZelluX

Reputation: 72775

Update additional association attribute before saving a model

I have three models Article, Author, and AuthorLine presenting relationship between article and its authors, in a many-to-many mapping.

class Article < ActiveRecord::Base                                                                                                                                                                
  has_many :author_lines, :dependent => :destroy                                                                                                                                                                          
  has_many :authors, :through => :author_lines, :dependent => :destroy, :order => 'author_lines.position'   

  attr_accessor :author_list
end

class Author < ActiveRecord::Base                                                                                                                                                                 
  has_many :author_lines                                                                                                                                                                          
  has_many :articles, :through => :author_lines                                                                                                                                                   
end

class AuthorLine < ActiveRecord::Base                                                                                                                                                             
  validates :author_id, :article_id, :position, :presence => true

  belongs_to :author, :counter_cache => :articles_count                                                                                                                                           
  belongs_to :article                                                                                                                                                                             
end

The AuthorLine model has an additional attribute position, which tells the order of authors for an article.

Here is what I am doing to create an article with given author names, in article.rb:

def author_list=(raw)                                                                                                                                                                           
  self.authors.clear                                                                                                                                                                            
  raw.split(',').map(&:strip).each_with_index do |e, i|                                                                                                                                         
    next if e.blank? 
    author = Author.find_or_create_by_name(e)                                                                                                                                                   

    #1                                                                                                                                                                      
    self.authors << author                                                                                                             

    #2
    # AuthorLine.create(:author_id => author.id, :article_id => self.id, :position => i)                                                                                                        
  end                                                                                                                                                                                           
end

The problem is I have no idea when to update the position attributes of corresponding AuthorLines. if I remove the line #1 and uncomment the line #2, the created AuthorLine may have a nil arctile_id since self.id may not be given.

Upvotes: 0

Views: 1196

Answers (1)

Mark Tabler
Mark Tabler

Reputation: 1429

I'd probably move the code for creating AuthorLines to an after_create hook in your Article model. If I understand the problem correctly, something like this should do the trick:

after_create :set_author_line_positions

def set_author_line_positions
  self.authors.each_with_index do |author, index|
    existing_author_line = AuthorLine.where("author_id = ? and article_id = ?", author.id, article.id).first
    if existing_author_line
      existing_author_line.update_attributes(:position => index)
    else
      AuthorLine.create(:author_id => author.id, :article_id => self.id, :position => index)
    end
  end
end

That way, you only wind up setting the AuthorLine positions after your article is already created and has an ID. This also checks to make sure an AuthorLine has already been created; I believe that an AuthorLine would get created every time an author is added to an article but I like to have very explicit checks in callbacks like this.

Upvotes: 1

Related Questions