如何在Ruby on Rails中的二级模型上设置counter_cache

时间:2017-08-04 07:13:23

标签: ruby-on-rails ruby-on-rails-5.1 coding-style

问题

好的,所以我有一个奇怪的结构问题,而且我知道的rails约定没有帮助。

在我的系统中,用户拥有服务器的成员资格。这通常是一个HABTM关联,但我通过Memberships模型使用has_many来简化一些事情(根据http://blog.flatironschool.com/why-you-dont-need-has-and-belongs-to-many/的建议)。成员资格包含有关用户与服务器关系的元数据。对于我正在做的事情,这非常有效。

现在,我遇到的麻烦是,每个成员资格都可以包含许多用户在服务器上使用的配置文件。由于配置文件与一个用户和一个服务器相关联,因此我只有配置文件belongs_to成员资格。我想要做的是在服务器模型上使用counter_cache作为配置文件,因为Rails似乎不断运行此查询:

SELECT COUNT(*) FROM "profiles" INNER JOIN "memberships" ON "profiles"."membership_id" = "memberships"."id" WHERE "memberships"."server_id" = $1

我的尝试

首先,Rails没有belongs_to through,因此配置文件不能通过成员资格模型“owner_to”服务器,因此我无法在那里提供counter_cache。

我的第二个想法是使用服务器模型上的委托来委托:profiles_count到成员资格,只是通过成员资格来执行counter_cache,但我无法让它工作。

我的第三次尝试是将server_id委托给:Profile中的成员身份,看看是否允许我与Server模型建立belongs_to关联。不好。这里有趣的是,没有抛出任何错误,它只是在没有警告的情况下回滚新配置文件中的保存。

我能想到的另一件事是将server_id和user_id列添加到“个人档案”模型,从而有效地从成员资格中复制HABTM关联。我不确定这种互联性水平会有多稳定。这实际上是在两个方向上创建has_many关联,循环引用链:membership - > server-> profile-> membership ....

问题

我是否错过了以这种方式处理counter_caching的特定约定,或者我选择了:

  1. 通过内部联接不断从数据库查询计数的现状
  2. 冒着无限的关联循环
  3. 支持代码

    以下是修剪到相关位的相关类:

    class User < ApplicationRecord
      has_many :memberships, dependent: :destroy
      has_many :servers, through: :memberships
      has_many :profiles, through: :memberships
    end
    
    class Server < ApplicationRecord
      has_many :memberships
      has_many :users, through: :memberships
      has_many :profiles, through: :memberships
    
      # This did not work with either singular or plural membership:
      # delegate :profiles_count, to: :membership
    end
    
    class Profile < ApplicationRecord
      belongs_to :membership, counter_cache: true
    
      # The following also does not work:
      # belongs_to :server, counter_cache: true
      # delegate :server_id, to: :membership
    
      scope :for_user, -> (id) { joins(:membership).where(memberships: { user_id: id }) }
      scope :for_server, -> (id) { joins(:membership).where(memberships: { server_id: id}) }
    end
    
    class Membership < ActiveRecord::Base
      belongs_to :user
      belongs_to :server, counter_cache: true
      has_many :profiles, dependent: :destroy
    end
    

    以下是db / schema.rb中的create_tables,为了简洁,将其截断为关联字段:

      create_table "users", force: :cascade do |t|
        # ...
      end
    
      create_table "servers", force: :cascade do |t|
        # ...
        t.integer "memberships_count", default: 0
        t.integer "profiles_count", default: 0
      end
    
      create_table "profiles", force: :cascade do |t|
        t.bigint "membership_id"
        # ...
      end
    
      create_table "memberships", force: :cascade do |t|
        t.bigint "user_id"
        t.bigint "server_id"
        # ...
        t.integer "profiles_count", default: 0
      end
    

0 个答案:

没有答案