#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
becauseAssignedComment
иUnassignedComment
иметь разные атрибуты.
Ответ №2:
Средство доступа, созданное с помощью has_many
возвращает an ActiveRecord::Relation
, которое в основном является просто оболочкой для SQL-запроса.
Вы можете легко написать метод для объединения всех результатов отношений в массив.
def comments
self.assigned_comments.to_a self.unassigned_comments.to_a
end
Но у этого есть несколько недостатков:
-
Он всегда загружает и инициализирует все
assigned_comments
иunassigned_comments
в память. -
У него нет ни одного из
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