如何在 rails 中显示父记录及其子记录总数

How to display Parent record with total number of its Child record in rails

class Attachment < ActiveRecord::Base
  belongs_to :user, foreign_key: :creator_id
  belongs_to :deal_task, foreign_key: :relation_id
end
class DealTask < ActiveRecord::Base
   has_many :attachments, foreign_key: :relation_id
end

我的父 table 称为 DealTask​​,子 table 称为 Attachment

我想要一个包含相关附件总数的 DealTask​​ 记录列表

试试这个

DealTask.all.map { |deal_task| deal_task.attachments.ids }.count

DealTask.first.attachments.count #This will give count of attachemenets

#To show all records and all the count

DealTask.find_each do |dt|
      print dt.inspect
      print "\n"
      print dt.attachments.count
end

或者

DealTask.joins(:attachments).select("deal_tasks.*, count(attachements.id) as count").group("deal_tasks.id")

为了更好的格式

DealTask.joins(:attachments)
        .select("deal_tasks.id, deal_tasks.name, count(attachements.id) as attachments")
        .group("deal_tasks.id")
        .collect(&:attributes)
#This will gve you something like

[
{"id"=>34332630, "name"=>"some name", "attachments"=>1}, 
{"id"=>71649461, "name"=>"some name", "attachments"=>1}
]

这会快很多,因为您在单个查询中获取所有数据

DealTask.all.map do |deal_task|
  deal_task.
    attributes.
    with_indifferent_access.
    slice(:id, :name).
    merge!(total_attachment: deal_task.attachments.count)
end

或者,如果您不关心无差别访问并且不介意拥有所有 DealTask 属性,您可以在一行中编写:

DealTask.all.map{|deal_task| deal_task.attributes.merge!(total_attachments: deal_task.attachments.count)}

分解...

DealTask.all.map do |deal_task|
  ...
end

即将 return 和 arrayarray 将包含 do 块的结果。

deal_task.
  attributes.
  with_indifferent_access

为您提供散列中每个 deal_task 的属性,可以使用 stringssymbols 访问(因此,"indifferent_access")。

deal_task.
  attributes.
  with_indifferent_access.
  slice(:id, :name)

仅保留 deal_task 散列的 :id:name

merge!(total_attachments: deal_task.attachments.count)

使用键 total_attachments.

将附件计数添加到您的散列中

结果应该类似于:

[
  {id: 1, name: 'name1', total_attachments: 12},
  {id: 2, name: 'name2', total_attachments: 3}
]

我找到了 Parent child 关系数

的最佳解决方案
counter_cache: true

因为上述所有查询都需要太多时间从数据库加载

所以你们一定更喜欢用这个

1-> 在 Parent table 中添加一列称为 DealTask​​

rails g migration AddAttachmentsCountToDealTask attachments_count:integer

2-> 打开迁移添加编辑

class AddAttachmentCountToDealTask < ActiveRecord::Migration[5.0]

 def up
  add_column :deal_tasks, :attachments_count, :integer, default: 0
  DealTask.reset_column_information
  DealTask.find_each do |deal_task|
    DealTask.reset_counters deal_task.id, :attachments
  end
 end
 def down
  remove_column :deal_tasks, attachments_count
 end
end

因此,当您回滚迁移时,它不会引发错误或异常

你也可以使用任何循环来代替

find_each, DealTask.all.each do...end

但是是的,虽然 重置计数器 必须使用 class 名称,如

DealTask.reset_counters

3-> 设置计数器缓存

class Attachment < ActiveRecord::Base
 belongs_to :deal_task, foreign_key: :relation_id, counter_cache: true
end
class DealTask < ActiveRecord::Base
 has_many :attachments, foreign_key: :relation_id
end

假设您的模型名称是 sub_tasks 而您的 counter_cache 列必须是

sub_tasks_count

如果您想要自己的列名,则必须在 counter_cache

中指定该列名

假设列名是 total_subtasks 而不是

belongs_to :deal_task, foreign_key: :relation_id, counter_cache: :total_subtasks

并相应地进行更改以更新 counter_cache

现在,当您添加任何附件attachments_count列增加1 这是由 **counter_cache

自动完成的

有一个问题 **当你删除任何child时counter_cache无法减少**

所以对于该解决方案进行回调

class Attachment < ActiveRecord::Base
 belongs_to :deal_task, foreign_key: :relation_id, counter_cache: true
 before_destroy :reset_counter
 private
 def reset_counter
  DealTask.reset_counters(self.relation.id, :attachments)
 end
end

因此,当您删除任何附件时,它会通过 relation_id 为它的 Parent 重置 countet_cache,即 parent_id 或Foreign_key 附件

了解更多信息 在 Railscast counter cache 23

上观看视频