Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

reset_counters raise undefined method error, but works fine on similiar relation

I try to add counter_cache to User to count owned Projects.

User has many projects, Project has many Users, but Project belongs to one of them.

So i wrote migration for adding extra count fields for counter_cache.

Comments has no trouble with reset_counter.

But User.reset_counters user.id, :projects raise NoMethodError: undefined method `counter_cache_column' for nil:NilClass.

Also counter_cache on projects is working OK. If i add extra project it will increment projects_count field, if i delete one - decrement.

Models

class User < ActiveRecord::Base
  has_many :projects, :through => :team_members
  has_many :team_members

class TeamMember < ActiveRecord::Base
  belongs_to :user
  belongs_to :project

class Project < ActiveRecord::Base
  has_many :users, :through => :team_members
  has_many :team_members
  belongs_to :leader, :class_name => :User, :counter_cache => true

class Comment < ActiveRecord::Base
  belongs_to :user, :counter_cache => true

Migration

class AddCounters < ActiveRecord::Migration
  def up
    add_column :users, :comments_count, :integer, :default => 0
    add_column :users, :projects_count, :integer, :default => 0 

    User.reset_column_information

    User.find_each do |user| 
      User.reset_counters user.id, :comments #all fine

      User.reset_counters user.id, :projects #raise NoMethodError
                                             #undefined method `counter_cache_column'
                                             #for nil:NilClass
    end
  end

  def down
    remove_column :users, :projects_count
    remove_column :users, :comments_count
  end
end

i tried to switch from :counter_cache => true to :counter_cache => :projects_count.

Same thing.

like image 855
Aristarhys Avatar asked May 11 '14 19:05

Aristarhys


1 Answers

Should you not tell the models that they have many :team_members for the :through calls to work? Try adding:

has_many :team_members

to both the User and Project models.


EDIT: Looking at your setup more closely, it seems like the issue is that you're setting the counter cache on the :leader relation, which has no corresponding inverse on the User side. I'm not exactly sure which way you want it, but here is how I picture the options:

Option 1: Give the user a separate :lead_projects association

class User < ActiveRecord::Base
  has_many :lead_projects, :class_name => :Project, :inverse_of => :leader

class Project < ActiveRecord::Base
  belongs_to :leader, :class_name => :User, :counter_cache => :lead_projects_count, :inverse_of => :lead_projects

and a migration adding :lead_projects_count to the :users table.

Option 2: Make the :projects_count counter go through TeamMembers

As per this similar answer:

class User < ActiveRecord::Base
  has_many :team_members
  has_many :projects, :through => :team_members

class TeamMember < ActiveRecord::Base
  belongs_to :user, :counter_cache => :projects_count
  belongs_to :project

class Project < ActiveRecord::Base
  has_many :team_members
  has_many :users, :through => :team_members

With this option, I think your current migration is sufficient.


Note: I'm just writing code as I go here; please comment if I got the syntax wrong so I can edit my answer for future readers.

like image 85
lime Avatar answered Sep 26 '22 18:09

lime