I have two ActiveRecord models (Book, Ad) which are associated. In my query I want to fetch books which has got 1 or more ads. What would be the best way to do this?
# in controller:
#books = Book.where(book has got 1 or more ads).last(20)
# Book model:
class Book < ActiveRecord::Base
has_many :ads, :dependent => :destroy
...
end
# Ad model:
class Ad < ActiveRecord::Base
belongs_to :book
...
end
One option is to use joins:
Book.joins(:ads)
joins allows you to do all of this in one query, and this would give you all Books that have a book_id on Ad set.
So for your controller, you'd have:
#books = Book.joins(:ads).last(20)
in your controller
subquery = Ad.select("book_id").group(:book_id).having("COUNT(*) >= 1").to_sql
#books = Book.where("id IN (#{subquery})").last(20)
Another simple solution would be
Ad.all.map{|ad| ad.book}.uniq.last(20)
This will return the last 20 unique books in an Array because if a Ad exists then it has a Book thus the Book has 1 or more Ads
Related
Sorry to ask this question but I'm really newbie with Ruby and I need help to update several records on my database.
I'm using ActiveRecord to query the database. Let say I have a table Product that contains SubProduct that also contains SubSubProduct. Now I would like to write a simple query to get all SubSubProduct of Product.
To get a list of SubSubProduct I usually do this
ssp = SubSubProduct.where(sub_sub_type: "example")
Now to use a where clause on relational element how can I do
ssp = SubSubProduct.where(sub_sub_type: "example", SubProduct.Product.type: "sample")
Set up ActiveRecord associations in your models:
#app/models/product.rb:
class Product < ActiveRecord::Base
has_many :sub_products
end
#app/models/sub_product.rb:
class SubProduct < ActiveRecord::Base
belongs_to :product
end
#app/models/sub_sub_product.rb:
class SubSubProduct < ActiveRecord::Base
belongs_to :sub_product
end
What you wanted:
ssp = SubSubProduct.where(sub_sub_type: "example", SubProduct.Product.my_type: "sample")
The correct syntax:
ssp = SubSubProduct.includes(sub_product: :product).where(sub_sub_type:"example", products: {my_type: "toto"})
This is performed via associations.
class SubSubProduct
has_many :products
end
Then you can do things like
sub_product.products
and it will product all the products associated with them.
Try a nested includes:
`Product.includes(subproducts: :subsubproducts)'
For this, you'll want to set up ActiveRecord associations in your models:
In product.rb:
class Product < ActiveRecord::Base
has_many :sub_products
has_many :sub_sub_products, through: :sub_products
end
In sub_product.rb:
class SubProduct < ActiveRecord::Base
belongs_to :product
has_many :sub_sub_products
end
In sub_sub_product.rb:
class SubSubProduct < ActiveRecord::Base
belongs_to :sub_product
end
Then, if you have a Product and want its SubSubProducts, you can use:
# p is a Product object
p.sub_sub_products
this is my first question on StackOverflow :)
I'm building a Rails 4 app, having trouble to figure out a good way to load records from mutilple data models. I could hard code SQL statements like an inner join, but wondering if there's any better way. Searched in existing questions on SO, but didn't find a match.
Here are my models:
class Person < ActiveRecord::Base
has_many :addresses
end
class Address < ActiveRecord::Base
belongs_to :person
belongs_to :city
end
class City < ActiveRecord::Base
has_many :addresses
end
Question: given a person Id, how should I load its associated addresses with the city information?
Address.includes(:persons,:cities).where(person_id: person.id)
this is one of many ways.
Here are my models:
Patient:
has_many :patient_records
PatientRecord:
belongs_to :patient
has_many :progress_reports
ProgressReport:
has_one :patient_record
The query I am trying to produce is to get all patients where progress_reports are older than or equal to 7 days from now (using a date_of_report column in progress_reports) while including or joining the patient record table... I have been working on this for so long that I have ran into a brick wall.
I would try:
scope :recent_patients, lambda
{ |since_when| join(:progress_reports)
.where("progress_reports.created_at >= ?", since_when)}
in your Patient model
reports = ProgressReport.where(:created_at > 7.days.ago).all
if you want to get each patient that belongs to each record, do:
reports.each do |r|
puts r.patient.name
end
I need to query a database table and get the rows ordered by a count of an association. Is there a Rails (like Active Record Query) way to do this?
My models and their associations are as follows:
class User < ActiveRecord::Base
has_one :business
end
class Business < ActiveRecord::Base
has_many :postulations
end
class Postulation < ActiveRecord::Base
belongs_to :business
end
I need to get a number of Users ordered by the amount of Postulations that their Business has. Is there a clean way to do this or do I just have to query with find_by_sql?
Thank you.
User.includes(:business => :postulations).group("users.id").order("count(postulations.id) desc").limit(20)
This will probably work
I'm trying to figure out the best way to setup my database and models for the following scenario.
A user can like an infinite number of songs.
A song can be liked once by an infinite number of users.
I have these tables:
songs, users, likes etc... Following RoR conventions.
The table named likes has these foreign keys: user_id, song_id. And also a field named 'time' to save a timestamp when the song was liked.
I'm not sure of how to do this, I would like to be able to use code like this in my controllers:
User.find(1).likes.all
This should not return from the likes table, but join the songs table and output all the songs that the user likes.
What are the best practises to achieve this in Ruby on Rails following their conventions?
Unless you need to act specifically on the likes table data, the model itself is probably not necessary. The relationship is easy:
class User < ActiveRecord::Base
has_and_belongs_to_many :songs
end
class Song < ActiveRecord::Base
has_and_belongs_to_many :users
end
This will join through the currently non-existent song_users table. But since you want it to join through likes you can change each one to this:
has_and_belongs_to_many :songs, :join_table => 'likes'
If you want to be able to call User.find(1).likes and get songs, then change the user's version to this:
class User < ActiveRecord::Base
has_and_belongs_to_many :likes, :join_table => 'likes', :class_name => 'Song'
end
And you could change the songs version to something like this:
class Song < ActiveRecord::Base
has_and_belongs_to_many :liked_by, :join_table => 'likes', :class_name => 'User'
end
This will give you Song.find(1).liked_by.all and give you the users (You could keep it to users if you wanted using the first version)
More details on habtm relationships can be found here: http://apidock.com/rails/ActiveRecord/Associations/ClassMethods/has_and_belongs_to_many
Edit to add
If you want to act on the join table for whatever reason (you find yourself needing methods specifically on the join), you can include the model by doing it this way:
class User < ActiveRecord::Base
has_many :songs, :through => :likes
has_many :likes
end
class Like < ActiveRecord::Base
belongs_to :user
belongs_to :song
end
class Song < ActiveRecord::Base
has_many :users, :through => :likes
has_many :likes
end
This will let you do User.find(1).songs.all, but User.find(1).likes.all will give you the join data