Aggregating multiple models in to one association - ruby-on-rails-3

I want to add a has_many which will return an ordered collection of different types, similar to a polymorphic association.
class Group < AR:Base
has_many ??
end
class Picture < AR::Base; belongs_to :group; end
class Video < AR::Base; belongs_to :group; end
class Audio < AR::Base; belongs_to :group; end
How do I have group return all the pictures, videos and audio which belong_to it using SQL. I don't really want to have to resort to using Ruby since I also want to have the collection of 'media items' ordered with acts_as_list or similar.

Use STI (single table inheritance).
class Group < AR:Base
has_many :media_items
end
class MediaItem < AR::Base; belongs_to :group; end
class Picture < MediaItem; end
class Video < MediaItem; end
class Audio < MediaItem; end
You just need to add the type column to your DB schema for that.

Related

How to manually join two different table with different attribute name in Ruby on Rails controller

I am currently making a website that runs on Ruby on Rails. I am facing some issues while I was trying to join two tables, Rates and Locations, that I have with two different attributes name.
Rates: id rater_id rateable_id (and a few more attributes in this table)
Locations: id title body user_id (and a few more attributes in this table)
Here is the query that I am trying to do in SQL.
SELECT *
FROM rates, locations
WHERE rates.rater_id = locations.user_id AND rates.rateable_id = locations.id
I have read the official active record documents that provided by rubyonrails.org. I have tried doing these, but it does not work. Here is the code that I am trying to implant in app\controllers\users_controller.rb
#join_rating = Rate.joins(:locations).where("rates.rateable_id = locations.id AND rates.rater_id = locations.id")
#all_rating = #all_rating.where(rater_id: #user)
#count_all_rating = #all_rating.count
#join_rating, is trying to join the attributes with different names.
#all_rating, is trying to filter which location to show using the user ID
#join_rating, is trying to calculate the total numbers of locations that are rated by the user
Assume that everything is setup correctly and the only error is in the query that I am trying to do, how should I rewrite the statement so that I am able to show the locations that the user has rated using #all_rating.
Thank you!
A few points:
When in ActiveRecord you're starting a statement with the Rate class, it means the result is going to be a collection of Rate objects. So if you're trying to show locations, you should start with a Location class.
#locations_user_rated = Location.joins('INNER JOIN rates ON
rates.rateable_id = locations.id').where('rates.rater_id' => #user)
And if your ActiveRecord associations are well defined, you could simply do:
#locations_user_rated = Location.joins(:rates).where('rates.rater_id' => #user)
"Well defined" simply means you'll need to do something like the following. Note that I am not sure I understand your model relationships correctly. I assume below that every location has multiple rates, and that the reason your Rate model has the field called rateable_id instead of a location_id is because you want :rateable to be polymorphic. This means you probably also have a rateable_type field in rates table.
class Location < ActiveRecord::Base
has_many :rates, as: :rateable
end
class Rate < ActiveRecord::Base
belongs_to :rateable, polymorphic: true
end
If this polymorphism is not the case, things should actually be simpler, and I highly recommend that you follow Rails's conventions and simply name the relationship field location_id on your Rate model instead of rateable_id. Then you can do:
class Location < ActiveRecord::Base
has_many :rates
end
class Rate < ActiveRecord::Base
belongs_to :location
end
If still you are not convinced about the field name, you can customize things and do:
class Location < ActiveRecord::Base
has_many :rates, foreign_key: :rateable_id
end
class Rate < ActiveRecord::Base
belongs_to :location, foreign_key: :rateable_id
end
You can find more about how to customize associations here, and here.
I highly recommend taking advantage of ActiveRecord's has_many, belongs_to, and has_many through: functionality.
If you set up a model for each of these tables, with the correct relationships:
class User < ActiveRecord::Base
has_many :ratings, foreign_key: :rater_id
has_many :rated_locations, through: ratings, class_name: Location.name, source: :rater
end
class Rating < ActiveRecord::Base
belongs_to :rater, class_name: User.name
belongs_to :location
end
class Location < ActiveRecord::Base
has_many :ratings
end
Then to access the locaitons that a user has rated, you just call
user.rated_locations

Chain scopes between models in Rails

I'm having some trouble querying between models in Rails. I have a class Message that belongs_to: booking. My goal is to add an active scope to Message that depends on a Booking scope.
class Booking < ActiveRecord::Base
has_one :event
has_many :messages
def self.active
includes(:event).
where('events.endtime >= ? AND status IS NOT ?'
Time.current.beginning_of_week,
statuses['canceled'])
end
end
class Message < ActiveRecord::Base
belongs_to :booking
belongs_to :person
self.active(person_id)
where(person_id: person_id).merge(Booking.active)
end
end
I want to find the Messages directed to a specific Person where the associated Booking is active. I therefore wish to use the Booking.active when creating Message.active.
Calling Message.active(1) with above implementation returns the following error:
Association named 'event' was not found on Message; perhaps you misspelled it?
Is there any way I can use Booking.active in the implementation of Message.active and get Messages returned?
If you are adding conditions on associations, you also need to join them, not only merge or include them, i.e. the following should work:
class Booking < ActiveRecord::Base
# ...
def self.active
joins(:event).
where('events.endtime >= ? AND status IS NOT ?'
Time.current.beginning_of_week,
statuses['canceled'])
end
end
class Message < ActiveRecord::Base
# ...
self.active(person_id)
where(person_id: person_id).joins(:booking).merge(Booking.active)
end
end
There is not much documentation on this, see this for more info.

ruby on rails - difference between using JUST 'belongs_to' versus using BOTH 'has_many' and 'belongs_to'?

What is the difference between using just belongs_to on one model versus having has_many on one and belongs_to on another?
As an example:
class Author < ActiveRecord::Base
end
class Book < ActiveRecord::Base
belongs_to :author
end
versus
class Author < ActiveRecord::Base
has_many :books
end
class Book < ActiveRecord::Base
belongs_to :author
end
Thank you.
guessing each of the methods would facilitate adding a different set of additional methods to the associated class
for ex, if had to guess, with belongs_to, you would, in part, get ability to call association on an instance of Book:
#book.author
with has_many, if I had to guess, you would, in part, be able to call association on instance of Author:
#author.books
also, http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-belongs_to
and
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_many
in case those may be of interest

Find records through two intermediate models in rails?

I'm having some trouble trying to fetch some models via SQL in rails and I was wondering if anyone knows of a good solution for this particular problem. Basically, these are what my classes look like:
class SubscriberList < ActiveRecord::Base
has_many :subscriptions
end
class Subscription < ActiveRecord::Base
has_many :messages
belongs_to :subscription_list
end
class Announcement < ActiveRecord::Base
has_many :messages
end
class Message < ActiveRecord::Base
belongs_to :subscription
belongs_to :announcement
end
Now, I want to access all the Announcements for a SubscriptionList excluding duplicates. Is this possible? Can it be done with a single SQL query or is that just wishful thinking?
For example here's what I want:
class SubscriptionList < ActiveRecord::Base
def announcements
Announcements.joins(:messages).where(message: { subscription: {subscription_list: self} })
end
end
I think your idea is correct in general. Try this variant
Announcements.
joins(messages: {subscription: :subscription_list}).
where(subscription_lists: {id: self.id})
# opposite
SubscriptionList.
joins(subscriptions: {messages: :announcement}).
where(announcements: {id: self.id})
Notes:
* these queries may return duplicates - so uniq can be added to them
* self can be omitted (I wrote it to show that this is id of instance and avoid missunderstanding)

Rails4 query help, find unique records with has_many though and a joining model

I have the following table structure
manufacturers --> products ---> available_sizes_products <-- sizes
and the following models
class Manufacturer < ActiveRecord::Base
has_many :products
end
class Product < ActiveRecord::Base
has_many :sizes, :through => :available_sizes_products
has_many :available_sizes_products
end
class AvailableProductSize < ActiveRecord::Base
belongs_to :sizes
belongs_to :products
end
class Size < ActiveRecord::Base
has_many :products, :through => :available_sizes_products
has_many :available_sizes_products
end
I need to get a unique list of manufacturers, that have products in size "XL" or "L" for example.I'm getting lost in the chaining of joins etc.
class Manufacturer < ActiveRecord::Base
def self.with_sizes(sizes=[])
#sizes = Sizes.find(sizes)
...
end
end
Can someone help me with that ? Trying to do the Rails 4 way rather than drop down to SQL, since I need the query to run on several DBS
Thanks
First of all you have to use single form of noun in belongs_to expression.
And for the query try this one:
Manufacturer.includes(:products).where(products: (size: "XL"))
I use "includes" to avoid N+1 query. Otherwise it will send two queries: one for Manufacturers and one for products. Write back, if this one doesn't fit your need.
EDIT
BTW, if you want to use exactly joining, write joins instead of includes.
Everything is here:
http://guides.rubyonrails.org/active_record_querying.html#joining-tables
and here:
http://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations
After going through the docs for joins
this is what worked :
Manufacturer.joins(products: :sizes).where(sizes: {id:ids}).distinct
This Rails way returns the model correctly.