Is overriding an ActiveRecord relation's count() method okay?

核能气质少年 提交于 2020-01-04 14:15:03

问题


Let's say I have the following relationship in my Rails app:

class Parent < ActiveRecord::Base
  has_many :kids
end

class Kid < ActiveRecord::Base
  belongs_to :parent
end

I want parents to be able to see a list of their chatty kids, and use the count in paginating through that list. Here's a way to do that (I know it's a little odd, bear with me):

class Parent < ActiveRecord::Base
  has_many :kids do
    def for_chatting
      proxy_association.owner.kids.where(:chatty => true)
    end
  end
end

But! Some parents have millions of kids, and p.kids.for_chatting.count takes too long to run, even with good database indexes. I'm pretty sure this cannot be directly fixed. But! I can set up a Parent#chatty_kids_count attribute and keep it correctly updated with database triggers. Then, I can:

class Parent < ActiveRecord::Base
  has_many :kids do
    def for_chatting
      parent = proxy_association.owner
      kid_assoc = parent.kids.where(:chatty => true)
      def kid_assoc.count
        parent.chatty_kids_count
      end
    end
  end
end

And then parent.kids.for_chatting.count uses the cached count and my pagination is fast.

But! Overriding count() on singleton association objects makes the uh-oh, I am being way too clever part of my brain light up big-time.

I feel like there's a clearer way to approach this. Is there? Or is this a "yeah it's weird, leave a comment about why you're doing this and it'll be fine" kind of situation?


回答1:


Edit:

I checked the code of will_paginate, seems like it is not using count method of AR relation, but i found that you can provide option total_entries for paginate

@kids = @parent.kids.for_chatting.paginate(
  page:          params[:page], 
  total_entries: parent.chatty_kids_count
)

This is not working

You can use wrapper for collection like here https://github.com/kaminari/kaminari/pull/818#issuecomment-252788488​, just override count method.

class RelationWrapper < SimpleDelegator
  def initialize(relation, total_count)
    super(relation)
    @total_count = total_count
  end

  def count
    @total_count 
  end
end

# in a controller:
relation = RelationWrapper.new(@parent.kids.for_chatting, parent.chatty_kids_count)


来源:https://stackoverflow.com/questions/46922421/is-overriding-an-activerecord-relations-count-method-okay

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!