Модель has_many И has_many: через в одном? - PullRequest
1 голос
/ 23 декабря 2011

У меня есть набор категорий, принадлежащих к набору категорий.Эти категории сами могут иметь сочетание категорий (самообращения), а также вопросов.Модели и их отношения определяются и визуализируются следующим образом:

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 (см. Ниже пример).* находит только прямые категории потомков из набора категорий.Кроме того, Category.find (id) .questions возвращает только вопросы для этой категории.

Я пытался перезаписать метод .questions для категорий, но это нене кажется, что отношения очень рельсовы, и должен быть лучший способ сделать это?Это означает, что я не могу использовать синтаксис стиля CategorySet.includes (: questions) .all, который значительно снижает нагрузку на сервер базы данных

1 Ответ

3 голосов
/ 23 декабря 2011

Подход 1

Используйте 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 , чтобы получитьсписок дополнительных столбцов, необходимых для гема.

Подход 2

Подход 1 загружает все вопросы в память и не поддерживает разбиение на страницы в БД.

Вы можете повысить производительность, если не будете вести отдельную таблицу для 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 
...