Почему / как можно вызывать методы класса модели в коллекции / массиве, возвращаемом отношением? - PullRequest
4 голосов
/ 06 января 2011

Итак, сегодня я наткнулся на интересный фрагмент кода, и он привлек мое внимание. Я не думал, что это было возможно. Строка кода была:

@post.comments.approved_comments

Выглядит хорошо, верно? Но на самом деле происходит то, что метод approved_comments вызывается для экземпляра объекта Array, который возвращается отношением has_many (т. Е. Пост содержит много комментариев). Еще более интересно то, что approved_comments - это метод класса, определенный в модели Comment.

Что меня немного озадачило, так это то, что я думал, что вы не можете зацепить отношения, не напрямую, хотя бы потому, что отношения возвращают Array, а не ActiveRecord::Relation объект. Ранее я задавал вопрос, связанный с этим, и ответ, который мне дали, заключается в том, что вы можете зацепить отношения, используя метод scoped или один из многих методов, которые добавляются в массив из-за отношений (т.е. , Раздел 4.3.1 Методы, добавленные has_many ).

Меня также озадачило то, что approved_comments - это метод, который мы написали; это не метод, добавленный методом отношений has_many. Так как я могу получить к нему доступ? Еще более запутанно, как получается, что я могу получить к нему доступ с объекта Array?

Конечно, происходит какое-то волшебство. Мне интересно, может ли кто-нибудь указать мне какую-то документацию или у каких-нибудь экспертов есть ответ на этот вопрос. Не большая проблема, потому что это, кажется, работает довольно хорошо, но я действительно хотел бы знать, как просто обучиться.

Заранее спасибо за помощь!

код

Сообщение

class Post
  has_many :comments
end

Комментарий

class Comment
  def self.approved_comments
    where(:approved => true)
  end
end

Использование

@post.comments.approved_comments

Ответы [ 2 ]

2 голосов
/ 06 января 2011

Это связано с тем, как ActiveRecord загружает запрос.Он не выполняется, пока вы не вызовете метод для объекта.В результате он может построить весь запрос более эффективно.ОДНАКО, то, что вы пытаетесь сделать, легче сделать, используя область действия:

class Post
  has_many :comments
end

class Comment
  scope :approved_comments, where(:approved => true)
end

Теперь вы можете сделать Comment.approved_comments, чтобы получить ВСЕ одобренные комментарии, или @ post.comments.approved_comments, чтобы получить утвержденные комментарии дляСообщение.

Более того, область действия может принимать необязательные параметры.Если вы хотите, чтобы все сообщения были утверждены с момента последнего посещения пользователя (например):

class Comment
  scope :approved_since, lambda{ |date| where(['approved_at > ?', date]) }
end

Тогда вы можете позвонить: @post.comments.approved_since(@user.last_login) например

Посмотрите на RailsActiveRecord Named Scope для получения дополнительной информации.

1 голос
/ 06 января 2011

Этот здесь объясняет точно что вы спрашиваете http://railscasts.com/episodes/5-using-with-scope

...