In my rails app I have few related models for example:
Event
has_many :comments
has_many :attendents
has_many :requests
What I need is to order by 'created_at' but not only main model (Event) but also related models, so I will display on top of the list event with most recent activity i.e.: comments, attendents, requests
So if Event date is newer than any comment, request or attendent date this event will be on top.
But if there is an event with newer comment this one will be on top etc.
How should I implement such ordering?
EDIT
db is mysql
Thanks
I would place a column on the event for last_activity, and maintain it by touching from the associated models.
The alternative is to order by using a join or subquery against the other tables, which is going to require a database query that will be much less efficient than simply ordering by last_activity descending.
Event
.select("events.*")
.joins("LEFT OUTER JOIN comments ON comments.event_id = events.id")
.joins("LEFT OUTER JOIN attendents ON attendents.event_id = events.id" )
.joins("LEFT OUTER JOIN requests ON requests.event_id = events.id")
.group("events.id")
.order("GREATEST(events.created_at,
MAX(comments.created_at),
MAX(attendents.created_at),
MAX(requests.created_at)) DESC")
If I understand your question correctly, something like this
Firstly add this to your Models in question:
default_scope order('created_at DESC')
Then I would do some grouping:
#comments = Comment.all
#comments_group = #comments.group_by { |c| c.event}
And in your view you'll be able to loop through each block:
#comments_group.each do |event, comment|
- event.name
comment.each do|c|
- c.body
I did not test this but it should give you an idea.
Related
I have the following active record models:
class Catalog < ActiveRecord::Base
has_and_belongs_to_many :customers
end
class Customer < ActiveRecord::Base
has_and_belongs_to_many :catalogs
end
Now in my index, i want to list all customers sorted like this:
first the ones who already are member of the catalog, then all the others.
I have tried something like this:
#customers = Customer.all.joins('LEFT JOIN catalogs_customers ON catalogs_customers.customer_id = customers.id').order('catalogs_customers.catalog_id DESC, customers.company_name ASC')
That is near to my goal but i got all the customers who are member of a catalog (whatever it is) and then all the other customers.
Your question is a tiny bit unclear, but I can't comment to ask more, so I'll do my best regardless.
It sounds like you want to list all customers with priority given to those associated with one specific catalog, but your code samples don't tell us how you plan on setting that. I'll assume that you have an instance variable #catalog_id. Then you want to provide a condition on your join where you only select catalogs_customers with that catalog_id. So try something like:
#customers = Customer.all.joins('LEFT JOIN catalogs_customers ON catalogs_customers.customer_id = customers.id').
.where(catalogs_customers: { catalog_id: #catalog_id }).
order('catalogs_customers.catalog_id DESC, customers.company_name ASC')
Hope that helps.
I am implementing an availability model nested within a listing. Its for a rental app.
class Listing
has_many :availabilities, dependent: :destroy
end
class Availability
belongs_to :listing
end
availabilities table has start and end date columns.
I am writing a query through search form to find listings where availabilities are present and the date given in the form lies in between start and end dates fo those availabilities.
My query in a class method looks like:
def self.search(params)
date = params[:date]
listingsids = Availability.where('startdate <= ?', date).where('enddate >= ?', date).pluck('listing_id')
products = Listing.where(id: listingsids)
end
However i feel this is not efficient. I wish I can write Listing.joins(:availability) and then use it but rails won't allow it. I can only join the other way which will give me a relation with availability objects and I want listings i.e. parent resource.
How can I make it more efficient and reduce number of queries I am doing?
Will appreciate your help :)
You should be able to use joins on listing to get you availablity relations, joins works using the relation name, not the model name, so instead of joins(:availability) you should be using joins(:availabilities). Something like this should work and use just a single query for your case:
Listing.joins(:availablities).where('availability.startdate <= ?', date).where('availability.enddate >= ?', date)
notice that joins uses the relation name joins(:availabilities) but the string in the where uses the table name where('availability.startdate <=?', date)
Right now, I'm working on a simple app. It requires to get the associated objects ordered by the date that they we're added to the object. For that, I want to order them based on the pivot-table's id.
My app looks a bit like this:
class Product < ActiveRecord::Base
has_and_belongs_to_many :users
end
class User < ActiveRecord::Base
has_and_belongs_to_many :products
end
However, when a user wants to buy a product, I would add a new relation into the pivot table courses_users. When I then run #product.users, I will get them back in the order the users where created, not added as the relation.
I've tried creating a query scope, but it didn't work. I also tried to create a order on the has_and_belongs_to_many, as such:
has_and_belongs_to_many :users, order: 'course_users.id ASC'
But none of that seemed to work, no ORDER statement could be found in the logs.
Add the created_at field to your table.
rails g migration AddTimestampsToCourseUsers created_at:datetime
then you can
#product.users.order "course_users.created_at ASC"
I have this query put together which performs fine.
search = Artist.where(:status => "active").where("upcoming_events_count > 0").order("echonest_external_popularity DESC").limit(limit))
The issue is that I'd like to add one more check, which is on another table ArtistPhoto. The match needs to make sure that the Artist (from the artist_id) also contains a valid photo.
I have the query working find independently:
ArtistPhoto.where("artist_id = ? and artist_photos.primary = ?", self.id, true).first
If anyone can assist with getting the join into 1 query that would be great.
Thanks
Use joins to add the other association:
search = Artist.joins(:artist_photos).
where("artist_photos.primary = ?", true).
where(...) # other filters here
This answer assumes you have a has_many :artist_photos on your Artist model.
I am working on a rails project and am having some issues with the following join:
#page = Page.find(params[:id], :joins => "LEFT JOIN page_translations ON page_translations.page_id = pages.id")
For some reason its only pulling back everything from the Pages table.
Here is my model for Page
class Page < ActiveRecord::Base
has_many :users_pages
has_many :users, :through => :users_pages
has_many :page_translations
has_many :categories
accepts_nested_attributes_for :page_translations
accepts_nested_attributes_for :categories
end
Here is my model for PageTranslation
class PageTranslation < ActiveRecord::Base
belongs_to :pages
end
Thanks in advance for all of the help!
Edit (#thenduks)
The log runs two separate queries:
Page Load (0.5ms) SELECT `pages`.* FROM `pages` WHERE (`pages`.`id` = 1) LIMIT 1
PageTranslation Load (0.5ms) SELECT `page_translations`.* FROM `page_translations` WHERE (`page_translations`.page_id = 1)
Here is what my controller looks like:
#page = Page.find(params[:id], :include => :page_translations)
I was stumped about this same thing and wasted a few hours trying to figure it out. It turns out that using the joins method of the query interface doesn't initialize the models related to the tables being joined. You can see this by watching the SQL statements in the server console, or by even redirecting ActiveRecord logging to STDOUT in your Rails console.
I was very disappointed by this. It just doesn't seem like how the joins method should work -- it certainly wasn't what I was expecting. I was expecting it to eager load, since it was in the eager load section of the Edge Guides.
Anyway, I couldn't waste any more time trying to figure it out, so what I did instead is use the fancy query interface to simply build my query, used to_sql to get the SQL for my query, and then passed the SQL to select_all, which returns an array of hashes, where each element in the array (each hash) represents a row.
Example:
query = Post.joins("LEFT JOIN categories ON post.category_id = categories.id")
query.select("posts.*, category.category_name")
con = ActiveRecord::Base.connection
results = con.select_all(query.to_sql)
Results:
[{"id": 1, "title": "joins, why have you forsaken me", "category_name": "frustration"},{"id": 2, "title": "pizza", "category_name": "food"}]
To be honest, I would still like to know for certain if it is possible to do it the way we think it should work, or the way it ought to work. Otherwise, I see no reason for having a joins method, other than to help us build the query. So if any Rails gurus out there know how to use joins to populate models related to those tables, PLEASE LET ME (US) KNOW!
Anyway, I hope this helps you move along for now.
UPDATE: So I think I just figured it out. I stumbled across this blog post. As it turns out, when using the joins method of the query interface, Rails will actually add the columns you selected from the joined tables as attribute methods of the model being joined against.
Using the same example above, I can actually access the category_name of each post by simply calling post.category_name. #$%! Unbelievably simple, but no documentation whatsoever on this!
Here it is once again:
query = Post.joins("LEFT JOIN categories ON post.category_id = categories.id")
query.select("posts.*, category.category_name")
posts = query.all
# access a post's category name
puts posts[0].category_name
# this is what I thought I would be able to do
# without Rails making another query to the database
puts posts[0].category.category_name
I hope this helps! :)
How about:
Page.find( params[:id], :include => :page_translations )
Edit:
Ok, so some time recently the behavior of ActiveRecord when it comes to joins/includes seems to have changed. The guides still refer to being able to do this though two associations, as in has_many :orders, :include => :line_items and similar... but as far as including records from a has_many... After consulting with a co-worker we came across some info on the subject. Seems that the single monolithic queries were just getting too complex and ugly and it was causing problems for some of the fancier niceties that ActiveRecord gives you and duplicate rows, that kind of thing.
TL;DR: It doesn't work like that anymore. 2 queries is expected.