Как «объединить» результат двух ассоциаций моделей `has_many`?

#ruby-on-rails #ruby #activerecord #ruby-on-rails-4 #associations

#ruby-on-rails #ruby #activerecord #ruby-on-rails-4 #ассоциации

Вопрос:

Я использую Ruby on Rails 4.1, и я хотел бы «объединить» результат двух has_many ассоциаций моделей. То есть у меня есть следующие модели и ассоциации:

 class Article < ActiveRecord::Base
  has_many :assigned_comments
  has_many :unassigned_comments
end

class AssignedComment < ActiveRecord::Base
  belongs_to :article
end

class UnassignedComment < ActiveRecord::Base
  belongs_to :article
end
  

Я хотел бы реализовать «что-то», чтобы запустить @article.comments и заставить его возвращать как данные @article.assigned_comments , так и @article.unassigned_comments .

Как я могу это сделать? Есть какая-то обычная практика? Как насчет возвращаемых данных и запросов к базе данных?

Комментарии:

1. я не думаю, что это обычная практика. Вы можете сделать это с помощью find_by_sql и ОБЪЕДИНЕНИЯ

2. Как должен выглядеть find_by_sql UNION оператор and в моем случае? Какие могут быть недостатки?

3. find_by_sql может вызываться для любой активной записи (точно так find же), и вы можете передать ей любой оператор SQL, например Article.find_by_sql("select COUNT(*) from assigned_comments") . ОБЪЕДИНЕНИЕ — это стандартная вещь SQL (подробнее в Google). недостатками является то, что это нестандартные rails — он не вернется с аккуратным набором моделей, как это делает обычная ассоциация.

4. Другой альтернативой может быть настройка полиморфной ассоциации.

5. Как должны появляться polymorphic ассоциации в моем случае? Какие могут быть недостатки?

Ответ №1:

Если AssignedComment и UnassignedComment имеют одинаковые атрибуты, и состояние assigned может быть изменено на unassigned и в обратном случае, почему бы просто не добавить и приписать is_signed Comment и сделать что-то вроде этого:

 class Article < ActiveRecord::Base
  has_many :comments
end

class Comment < ActiveRecord::Base
  belongs_to :article
  scope      :assigneds,    -> { where(is_signed: true) }
  scope      :unassigneds,  -> { where(is_signed: false) }
end
  

С помощью scope вы можете сделать @article.comments.assigneds или @article.comments.unassigneds и, конечно, вы можете просто сделать @article.comments , чтобы получить все comments , что принадлежит @article

Комментарии:

1. Вы должны подчиняться закону деметры.

2. @Frozenna — Я не могу просто добавить атрибут is_signed к Comment because AssignedComment и UnassignedComment иметь разные атрибуты.

Ответ №2:

Средство доступа, созданное с помощью has_many возвращает an ActiveRecord::Relation , которое в основном является просто оболочкой для SQL-запроса.

Вы можете легко написать метод для объединения всех результатов отношений в массив.

 def comments
  self.assigned_comments.to_a   self.unassigned_comments.to_a
end
  

Но у этого есть несколько недостатков:

  1. Он всегда загружает и инициализирует все assigned_comments и unassigned_comments в память.

  2. У него нет ни одного из ActiveRecord::Relation удобных методов

В зависимости от того, насколько похожи AssignedComment столбцы и UnassignedComment , лучшим решением может быть объединение их в одну таблицу и подкласс.

 class Article < ActiveRecord::Base
  has_many :comments
  has_many :assigned_comments, -> { where(assigned: true) }
  has_many :unassigned_comments, -> { where(assigned: false) }
end

class Comment < ActiveRecord::Base
  belongs_to :article

  # Common functionality 
end

class AssignedComment < Comment
  default_scope { where(assigned: true) }
  before_save -> { self.assigned = true }

  # Functionality specific to AssignedComment
end

class UnassignedComment < Comment
  default_scope { where(assigned: false) }
  before_save -> { self.assigned = false }

  # Functionality specific to UnassignedComment
end
  

Ответ №3:

Я знаю, что это может быть не ответом на ваш вопрос. Это краткое описание STI для вашего случая.

Следует учитывать наследование одной таблицы при работе с классами моделей, которые имеют большую часть одинаковой функциональности и полей данных.

http://eewang.github.io/blog/2013/03/12/how-and-when-to-use-single-table-inheritance-in-rails/

 class Article < ActiveRecord::Base
  has_many :comments
end

class Comment < ActiveRecord::Base
  belongs_to :article
  scope :assigned, where(type: "AssignedComment")
  scope :unassigned, where(type: "UnassignedComment")
end

class AssignedComment < Comment

end

class UnassignedComment < Comment

end

all = @article.comments
assigned = @article.comments.assigned
unassigned = @article.comments.unassigned