Как выполнить несколько сложный запрос области ActiveRecord для полиморфной ассоциации? - PullRequest
3 голосов
/ 28 сентября 2011

Итак, у меня есть модель Notification, которая является полиморфной, и я хочу иметь возможность отфильтровывать уведомления, которые имеют notifiable_type Comment, где comment.user == current_user.Другими словами, я хочу, чтобы все записи уведомлений - кроме тех, которые ссылаются на комментарии, сделанные текущим пользователем.

class Notification

  belongs_to :notifiable, :polymorphic => true

  scope :relevant, lambda { |user_id|
    find(:all, :conditions => [
      "notifiable_type != 'Comment' OR (notifiable_type = 'Comment' AND " <<
        "comments.user_id != ?)",
      user_id ],
      :include => :comments
    )
  }

end

Я не понимаю, что мне нужно сделать, чтобы получить доступ кКомментарии?Мне нужно сказать ActiveRecord внешнему соединению модель комментария на notifiable_id.

Ответы [ 3 ]

5 голосов
/ 29 сентября 2011

Во-первых, лямбда-области с параметрами устарели.Вместо этого используйте метод класса:

class Notification
  belongs_to :notifiable, polymorphic: true

  def self.relevant(user_id)
    # ...
  end
end

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

Далее find(:all) устарело, как и :conditions.Сейчас мы используем запросов ActiveRelation .

К сожалению, API ActiveRecord::Relation недостаточно надежен, чтобы делать то, что вам нужно, поэтому вместо этого нам придется перейти на ARel.Немного сложно, но вы определенно не хотите делать подстановку строк из соображений безопасности.

class Notification
  belongs_to :notifiable, polymorphic: true

  def self.relevant(user_id)
    n, c = arel_table, Comment.arel_table
    predicate = n.join(c).on(n[:notifiable_id].eq(c[:id]))

    joins( predicate.join_sql ).
    where{ ( notifiable_type != 'Comment' ) | 
      (( notifiable_type == 'Comment' ) & ( comments.user_id == my{user_id} ))
    }
  end
end

Я использую комбинацию ARel и Squeel здесь.Squeel настолько хорош, что это должно быть основной особенностью Rails.Я пытался написать предложение where без Squeel, но это было так сложно, что я сдался.

Сложно протестировать что-то подобное без вашего проекта, но, надеюсь, это должно как минимум приблизить вас.

0 голосов
/ 29 сентября 2011

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

Notification.find_by_sql( "SELECT * from Notifications " <<
  "INNER JOIN comments ON notifiable_id = comments.id " <<
  "WHERE notifiable_type != 'Comment' " <<
    "OR (notifiable_type = 'Comment' AND comments.user_id = '#{user_id}')"
)
0 голосов
/ 29 сентября 2011

Упс, у вашего кода :include => :comments, множественное число, которое меня отбросило. Как насчет этого?

class Notification
  belongs_to :notifiable, :polymorphic => true

  scope :relevant, lambda { |user_id|
    find(:all, :conditions => [
      "notifiable_type != 'Comment' OR (notifiable_type = 'Comment' AND " <<
        "comments.user_id != ?)",
      user_id ],
      :include => :notifiable
    )
  }

end

... тогда Notification.relevant.first.notifiable должно работать. Из документов:

Стремительная загрузка поддерживается с полиморфными ассоциациями.

class Address < ActiveRecord::Base
  belongs_to :addressable, :polymorphic => true
end

Вызов, который пытается загрузить адресную модель

Address.find(:all, :include => :addressable)

Это выполнит один запрос для загрузки адресов и загрузки адресуемые с одним запросом для каждого адресуемого типа. Например, если все Адресация относится либо к классу Person, либо Company, то всего 3 запроса будут выполнены. Список адресуемых типов для загрузки определяется на обратной стороне загруженных адресов. Это не поддерживается если Active Record должен вернуться к предыдущей реализации нетерпеливо загружается и поднимет ActiveRecord::EagerLoadPolymorphicError. Причина в том, что Тип родительской модели - это значение столбца, поэтому его имя соответствует таблице. не может быть помещен в предложения FROM / JOIN этого запроса.

(Акцент мой.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...