我有一个带有一组关系的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
我想在一个查询中查找组织已阅读和评级的所有书籍。类似的东西:
organization.members.books
我最好将它与will_paginate一起使用,并按Readings类的评级排序。知道如何在没有自定义SQL的情况下执行此操作吗?
答案 0 :(得分:1)
Try the following relations:
class Organization < ActiveRecord::Base
has_many :memberships
has_many :users, through: :memberships
has_many :books, -> { uniq }, through: :users
end
class Membership < ActiveRecord::Base
belongs_to :organization
belongs_to :user
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
Now you can call @organization.books
to get all books for a specific organization.
I don't know exactly how you handle ratings, but you could add a scope called rated
to your Book
model and then call @organization.books.rated
to get all rated books for a specific organization. Here is an example of what that scope might look like:
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
That is just an example assuming you use some integer based rating system where a nil
rating means it is unrated. I also threw in the rated_above
and rated_below
scopes, which you may or may not find useful. You could use them like @organization.books.rated_above(6)
to only get the books with a rating greater than or equal to 6. Again, these are just examples, you might need to change them to work with your rating implementation.
Update
In the case where your ratings are stored on the Reading
model, you can change your Book
model to the following:
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
I am not sure if there is a simpler approach, but it gets the job done. Now the scopes should work as expected. Additionally, you can sort by rating like this: @organization.books.rated.order('average_rating DESC')
答案 1 :(得分:0)
这应该为您提供组织在一个查询中阅读的所有书籍。不确定您的评分是如何实施的。
Book.joins(:readings => {:user => :memberships})
.where(:readings => {:users => {:memberships => {:organization_id => @organization.id}}})