Marcelo
Marcelo

Reputation: 1772

ActiveRecord - has_many :through, :dependent => :destroy sql is not correct

I am trying to "counter cache" the number of posts in each tag. The after save callback is working but the after destroy is not. Looks like destroy sql is not correct.

class Post < ActiveRecord::Base
  has_many :post_tags, :dependent => :destroy
  has_many :tags, :through => :post_tags
end

class Tag < ActiveRecord::Base
  has_many :post_tags, :dependent => :destroy
  has_many :posts, :through => :post_tags
end

class PostTag < ActiveRecord::Base
  self.table_name =  :posts_tags
  belongs_to :post
  belongs_to :tag

  after_save :update_tag_posts_count
  after_destroy :update_tag_posts_count

  def update_tag_posts_count
    tag.posts_count = tag.posts.count
    tag.save
  end
end

The test fails

# @tag.posts_count == 10
Failure/Error: @tag.posts.first.destroy 
ActiveRecord::StatementInvalid:
 Mysql2::Error: Unknown column 'posts_tags.' in 'where clause': DELETE FROM `posts_tags` WHERE `posts_tags`.`` = NULL

The correct sql should be

DELETE FROM `posts_tags` WHERE `posts_tags`.`post_id` = {the post id}

Upvotes: 5

Views: 1152

Answers (3)

Paludis
Paludis

Reputation: 734

I had the exact same issue, the fix for me was to add a primary key column to the join table (PostTag in your case).

It seems that rails needs a primary key for the :dependent => :destroy option to work.

Upvotes: 3

James Daniels
James Daniels

Reputation: 6981

I'd suggest separate increment/decrement functions and not using count + save. This should function properly, perform well, not trigger side-effects on Tag, and not mess up your count in race conditions.

class PostTag < ActiveRecord::Base

  belongs_to :post
  belongs_to :tag

  after_create  :increment_tag_posts_counter
  after_destroy :decrement_tag_posts_counter

  private

  def increment_tag_posts_counter
    Tag.increment_counter :posts_count, post_id
  end

  def decrement_tag_posts_counter
    Tag.decrement_counter :posts_count, post_id
  end
end

Upvotes: 0

Neodelf
Neodelf

Reputation: 185

May be because in models you used

#post.rb
has_many :post_tags, :dependent => :destroy
#tag.rb
has_many :post_tags, :dependent => :destroy

instead of

#post.rb
has_many :posts_tags, :dependent => :destroy
#tag.rb
has_many :posts_tags, :dependent => :destroy

?

Upvotes: 0

Related Questions