2015-07-28 3 views
0

У меня есть приложение rails с набором отношений. Пользователи читают и ранжировать книги и организации коллекций пользователей, которые в совокупности имеют список книг, которые они читали/номинальными:Эффективный обход модели (объединения) в Rails

class Organization 
    has_many :users, through: memberships 
end 
class Membership 
    belongs_to :organization 
    belongs_to :user 
end 
class User 
    has_many :books, through: :readings 
    has_many :organizations, through: :memberships 
end 
class Readings 
    belongs_to :user 
    belongs_to :book 
end 
class Book 
    has_many :readings 
end 

Я хотел бы, по одному запросу, f все книги, которые организация читала и оценивала. Что-то вроде:

organization.members.books 

Я бы в идеале хотел бы использовать это с will_paginate и сортировать по рейтингам на классе чтения. Любая идея, как это сделать без пользовательского SQL?

+0

Не следует отношение в организации быть 'has_many: пользователи, через:: memberships' (или' has_many: члены, через:: членство, источник:: user')? –

+0

Да, обновил вопрос. – parker

ответ

1

Попробуйте следующие соотношения:

class Organization < ActiveRecord::Base 
    has_many :memberships 
    has_many :users, through: :memberships 
    has_many :books, -> { uniq }, through: :users 
end 
class User < ActiveRecord::Base 
    has_many :memberships 
    has_many :readings 
    has_many :organizations, through: :memberships 
    has_many :books, through: :readings 
end 
class Reading < ActiveRecord::Base 
    belongs_to :user 
    belongs_to :book 
end 
class Book < ActiveRecord::Base 
    has_many :readings 
    has_many :users, through: :readings 
    has_many :organizations, -> { uniq }, through: :users 
end 

Теперь вы можете звоните @organization.books, чтобы получить все книги для определенной организации.

Я не знаю точно, как вы справляетесь рейтинги, но вы можете добавить область под названием rated к вашей Book модели, а затем вызвать @organization.books.rated, чтобы получить все номинальные книги для конкретной организации. Вот пример того, что, что сфера может выглядеть следующим образом:

class Book < ActiveRecord::Base 
    has_many :readings 
    has_many :users, through: :readings 
    has_many :organizations, through: :users 

    scope :rated, -> { where.not(rating: nil) } 
    scope :rated_above, ->(rating) { where('rating >= ?', rating) } 
    scope :rated_below, ->(rating) { where('rating <= ?', rating) } 
end 

Это просто пример, если вы используете некоторое целое на основе рейтинговой системы, где nil рейтинга означает, что она нерейтинговая. Я также выбрал области rated_above и rated_below, которые вы можете или не можете найти полезными. Вы можете использовать их как @organization.books.rated_above(6), чтобы получить книги с рейтингом, большим или равным 6. Опять же, это просто примеры, вам может потребоваться изменить их для работы с вашей реализацией рейтинга.


Update

В том случае, если ваши оценки сохраняются на Reading модели, вы можете изменить свою Book модель на следующее:

class Book < ActiveRecord::Base 
    has_many :readings 
    has_many :users, through: :readings 
    has_many :organizations, -> { uniq }, through: :users 

    scope :rated, -> { with_ratings.having('COUNT(readings.rating) > 0') } 
    scope :rated_above, ->(rating) { with_ratings.having('average_rating >= ?', rating) } 
    scope :rated_below, ->(rating) { with_ratings.having('average_rating <= ?', rating) } 

    private 

    def self.with_readings 
    includes(:readings).group('books.id') 
    end 

    def self.with_ratings 
    with_readings.select('*, AVG(readings.rating) AS average_rating') 
    end 
end 

Я не уверен, если есть это более простой подход, но он выполняет свою работу. Теперь области действия должны работать должным образом. Кроме того, вы можете отсортировать по рейтингу: @organization.books.rated.order('average_rating DESC')

+0

Рейтинг не на самой книге, а на Рединге. Это для каждого пользователя, а не для книги. Это позволяет некоторым организациям оценивать книгу очень высоко, чтобы другие оценивали менее хорошо. Он выглядит здесь, как будто вы предполагали, что это книга. Я увижу, работает ли это с этой областью при чтении. – parker

+1

Да, это будет иметь больше смысла. Проверьте обновление, которое я сделал для своего ответа, и посмотрим, работает ли это. – dhouty

+0

Существует одна проблема с этим, а именно, что он возвращает список с дубликатами. Вы получаете одну книгу за каждое чтение. Я не могу исправить это несколькими моих попыток отличить выбор. Возможно, у вас есть предложение? – parker

0

Это должно дать вам все книги, которые организация прочитала в одном запросе. Не знаете, как ваши рейтинги реализованы.

Book.joins(:readings => {:user => :memberships}) 
    .where(:readings => {:users => {:memberships => {:organization_id => @organization.id}}}) 
Смежные вопросы