1

我有一组类别,属于一个类别集。这些类别本身可以包含类别(自我参照)和问题的组合。模型及其关系的定义和可视化如下:

class CategorySet < ActiveRecord::Base
  has_many :categories
end

class Category < ActiveRecord::Base
  belongs_to :category_set

  belongs_to :parent_category, :class_name => "Category"
  has_many :categories, :class_name => "Category", :foreign_key => "parent_category_id", dependent: :destroy

  has_many :questions, dependent: :destroy
end

class Question < ActiveRecord::Base
  belongs_to :category
end

Abbrev to CS, C and Q:

CS
  |- C
     |- Q
     |
     |- C
     |
     |- C
     |  |- Q
     |
     |- C
        |- Q
        |- Q

我希望能够询问 CategorySet.find(1).questions 并返回树中的所有问题,无论位置如何。我能想到的唯一方法是使用大量基于函数的请求,并且可能会在 sql 语句上过度使用(参见下面的示例)。

调用CategorySet.find(1).categories仅查找类别集的直接后代类别。此外,Category.find(id).questions仅返回该类别的问题。

我已经尝试过在类别上覆盖.questions方法,但这似乎不是很符合关系的关系,必须有更好的方法来做到这一点?这意味着我不能使用 CategorySet.includes(:questions).all 样式语法,这大大减少了数据库服务器上的负载

4

1 回答 1

3

方法一

为此使用awesome_nested_set

class CategorySet < ActiveRecord::Base
  has_many :categories
  def questions
    categories.map do |c|   
      c.self_and_descendants.include(:questions).map(&:questions)
    end.flatten
  end
end

class Category < ActiveRecord::Base
  awesome_nested_set
  belongs_to :category_set
  has_many :questions
end

class Question < ActiveRecord::Base
  belongs_to :category
end

请参阅awesome_nested_set文档以获取 gem 所需的其他列的列表。

方法二

方法 1 将所有问题加载到内存中,并且不支持基于 DB 的分页。

如果您避免为 CategorySet 维护单独的表,您可以获得更好的性能,因为每个类别都可以包含其他类别。

class Category < ActiveRecord::Base
  awesome_nested_set
  has_many :questions
  # add a boolean column called category_set

  def questions
    join_sql = self_and_descendants.select(:id).to_sql
    Question.joins("JOIN (#{join_sql}) x ON x.id = questions.id")  
  end
end

class Question < ActiveRecord::Base
  belongs_to :category
  validates :category_id, :presence => true, :category_set => true
end

# lib/category_set_validator.rb
class CategorySetValidator < ActiveModel::EachValidator  
  def validate_each(object, attribute, value)  
    if record.category.present? and record.category.category_set?
      record.errors[attribute] << (options[:message] || 
                    "is pointing to a category set") 
    end
  end 
end  

现在,您可以获得针对设置为的类别的问题

cs.questions.limit(10).offset(0)
cs.questions.paginate(:page => params[:page])  # if you are using will_paginate 
于 2011-12-23T07:29:41.367 回答