So, I''m putting together a blog, and I want the posts on it to be easily browsable by date. So far I''ve given each post attributes for the month and year it was created. That way, when someone visits example.com/blog/2008 Rails does a find_all_by_year and displays and paginates the results - similarly, when someone visits example.com/blog/2008/09 Rails does find_all_by_year_and_month and does the same. Now, I''m trying to change the blog layout so that it provides links to each month as well as the post count for that month. This would look something like: August 2008 (24) September 2008 (33) October 2008 (17) And so on. So, two questions: #1 - What''s the best way to do this? The most direct method I see is to do a Post.find_all_by_year_and_month(...).count for each month of each year, but that would be pretty abusive of the database, right? #2 - How do I do this and remain MVC? The above display is happening in the layout, and I know I shouldn''t be getting very code-heavy in the view, but I don''t know where else to put this kind of functionality. Thanks! Chris -- Posted via http://www.ruby-forum.com/.
Chris Hanks
2009-May-28 01:06 UTC
Re: Code in layout files, and minimizing database usage.
Ok, after some thinking and experimentation, I came up with: <% posts = Post.all(:select => "year, month", :order => "year, month").collect(&:attributes) %> <% (posts.first["year"]..posts.last["year"]).to_a.reverse.each do |year| %> <p><%= year %></p> <% posts_by_year = posts.select{|hash| hash["year"] == year} %> <ul class="nostyle"> <% (1..12).to_a.reverse.each do |index| %> <% posts_by_month = posts_by_year.select{|hash| hash["month"] == ("%02d" % index)}%> <% unless posts_by_month.length == 0 %> <li><%= link_to "#{Date::MONTHNAMES[index]} #{year}", month_path(year, ("%02d" % index)) %> (<%= posts_by_month.length %>)</li> <% end %> <% end %> </ul> <% end %> It''s working well. It''s all still stuck in the view, but oh well. If anyone knows a better place, let me know. -- Posted via http://www.ruby-forum.com/.
Marnen Laibow-Koser
2009-May-28 03:57 UTC
Re: Code in layout files, and minimizing database usage.
Chris Hanks wrote: [...]> It''s working well. It''s all still stuck in the view, but oh well.That''s way too much logic to put in a view, even by my standards. :)> If > anyone knows a better place, let me know.The easiest refactoring would be to put it in a helper. Or perhaps you can get creative with a class method on Post. Best, -- Marnen Laibow-Koser http://www.marnen.org marnen-sbuyVjPbboAdnm+yROfE0A@public.gmane.org -- Posted via http://www.ruby-forum.com/.
Julian Leviston
2009-May-28 07:17 UTC
Re: Code in layout files, and minimizing database usage.
On 28/05/2009, at 11:06 AM, Chris Hanks wrote:> > Ok, after some thinking and experimentation, I came up with: > > <% posts = Post.all(:select => "year, month", :order => "year, > month").collect(&:attributes) %>Any time you''re doing equalities against a model object (here Post), you could put it in the controller and assign it to an instance varaible. Also I''m not sure why you''re getting the attributes of the objects, when you could be getting the objects by themselves. controller: @posts = Post.all(:select => "year, month", :order => "year DESC, month DESC") view: <% @posts.group_by(&:year).each do |year, year_of_posts | %> <%= year %> <% year_of_posts.group_by(&:month) do |month, month_of_posts| %> <% month_of_posts.each do |post| %> <%= "some data about the posts or links or whatever %> <% end %> <% end %> <% end %> obviously you should change this to be more in line with what you actually want (probably the inner each is unnecessary for your requirements. Rather than reversing in ruby, use order with DESC sql fragment to get the data the way you want it from the database. This is what the database is good at. Also, have you thought of actually storing a datetime for the post, instead of doing all this crazy number stuff? Then you could use the strftime method on the datetime to print out the dates nicely. Much more elegant. Julian. ---------------------------------------------- Learn: http://sensei.zenunit.com/ Last updated 20-May-09 (Rails, Basic Unix) Blog: http://random8.zenunit.com/ Twitter: http://twitter.com/random8r> <% (posts.first["year"]..posts.last["year"]).to_a.reverse.each do | > year| > %> > <p><%= year %></p> > <% posts_by_year = posts.select{|hash| hash["year"] == year} %> > <ul class="nostyle"> > <% (1..12).to_a.reverse.each do |index| %> > <% posts_by_month = posts_by_year.select{|hash| hash["month"] => ("%02d" % index)}%> > <% unless posts_by_month.length == 0 %> > <li><%= link_to "#{Date::MONTHNAMES[index]} #{year}", > month_path(year, ("%02d" % index)) %> (<%= posts_by_month.length > %>)</li> > <% end %> > <% end %> > </ul> > <% end %> > > It''s working well. It''s all still stuck in the view, but oh well. If > anyone knows a better place, let me know. > -- > Posted via http://www.ruby-forum.com/. > > >
Why do you want to load all the posts into memory? For doing a count, rely on SQL instead of Ruby. It is faster and more efficient. Post.count(:conditions=>"xxx") translates into a select count(*) from posts where "xxx" When someone clicks on the month title, you can always do a query with a :limit to show the last 10 posts and a "all" link to show all posts in that month. I would have class level functions in the Post model provide me information regarding count and data. The model alone knows how to get that data, other parts of the application have no need to dig into those details. This will ensure your app doesn''t break if you change the implementation of Post in the future. On May 28, 12:17 pm, Julian Leviston <jul...-AfxEtdRqmE/tt0EhB6fy4g@public.gmane.org> wrote:> On 28/05/2009, at 11:06 AM, Chris Hanks wrote: > > > > > Ok, after some thinking and experimentation, I came up with: > > > <% posts = Post.all(:select => "year, month", :order => "year, > > month").collect(&:attributes) %> > > Any time you''re doing equalities against a model object (here Post), > you could put it in the controller and assign it to an instance > varaible. > > Also I''m not sure why you''re getting the attributes of the objects, > when you could be getting the objects by themselves. > > controller: > @posts = Post.all(:select => "year, month", :order => "year DESC, > month DESC") > > view: > > <% @posts.group_by(&:year).each do |year, year_of_posts | %> > <%= year %> > <% year_of_posts.group_by(&:month) do |month, month_of_posts| %> > <% month_of_posts.each do |post| %> > <%= "some data about the posts or links or whatever %> > <% end %> > <% end %> > <% end %> > > obviously you should change this to be more in line with what you > actually want (probably the inner each is unnecessary for your > requirements. > > Rather than reversing in ruby, use order with DESC sql fragment to get > the data the way you want it from the database. This is what the > database is good at. > Also, have you thought of actually storing a datetime for the post, > instead of doing all this crazy number stuff? Then you could use the > strftime method on the datetime to print out the dates nicely. Much > more elegant. > > Julian. > > ---------------------------------------------- > Learn:http://sensei.zenunit.com/ > Last updated 20-May-09 (Rails, Basic Unix) > Blog:http://random8.zenunit.com/ > Twitter:http://twitter.com/random8r > > > <% (posts.first["year"]..posts.last["year"]).to_a.reverse.each do | > > year| > > %> > > <p><%= year %></p> > > <% posts_by_year = posts.select{|hash| hash["year"] == year} %> > > <ul class="nostyle"> > > <% (1..12).to_a.reverse.each do |index| %> > > <% posts_by_month = posts_by_year.select{|hash| hash["month"] => > ("%02d" % index)}%> > > <% unless posts_by_month.length == 0 %> > > <li><%= link_to "#{Date::MONTHNAMES[index]} #{year}", > > month_path(year, ("%02d" % index)) %> (<%= posts_by_month.length > > %>)</li> > > <% end %> > > <% end %> > > </ul> > > <% end %> > > > It''s working well. It''s all still stuck in the view, but oh well. If > > anyone knows a better place, let me know. > > -- > > Posted viahttp://www.ruby-forum.com/.
Frederick Cheung
2009-May-28 08:23 UTC
Re: Code in layout files, and minimizing database usage.
On May 28, 9:03 am, Mukund <marut...-/E1597aS9LQAvxtiuMwx3w@public.gmane.org> wrote:> Why do you want to load all the posts into memory? For doing a > count, rely on SQL instead of Ruby. It is faster and more efficient. > > Post.count(:conditions=>"xxx") translates into a select count(*) from > posts where "xxx" >And you could even do Post.count(:all, :group => ''year, month'' ) (and then cache that :-) ) Fred> When someone clicks on the month title, you can always do a query with > a :limit to show the last 10 posts and a "all" link to show all posts > in that month. > > I would have class level functions in the Post model provide me > information regarding count and data. The model alone knows how to > get that data, other parts of the application have no need to dig into > those details. This will ensure your app doesn''t break if you change > the implementation of Post in the future. > > On May 28, 12:17 pm, Julian Leviston <jul...-AfxEtdRqmE/tt0EhB6fy4g@public.gmane.org> wrote: > > > On 28/05/2009, at 11:06 AM, Chris Hanks wrote: > > > > Ok, after some thinking and experimentation, I came up with: > > > > <% posts = Post.all(:select => "year, month", :order => "year, > > > month").collect(&:attributes) %> > > > Any time you''re doing equalities against a model object (here Post), > > you could put it in the controller and assign it to an instance > > varaible. > > > Also I''m not sure why you''re getting the attributes of the objects, > > when you could be getting the objects by themselves. > > > controller: > > @posts = Post.all(:select => "year, month", :order => "year DESC, > > month DESC") > > > view: > > > <% @posts.group_by(&:year).each do |year, year_of_posts | %> > > <%= year %> > > <% year_of_posts.group_by(&:month) do |month, month_of_posts| %> > > <% month_of_posts.each do |post| %> > > <%= "some data about the posts or links or whatever %> > > <% end %> > > <% end %> > > <% end %> > > > obviously you should change this to be more in line with what you > > actually want (probably the inner each is unnecessary for your > > requirements. > > > Rather than reversing in ruby, use order with DESC sql fragment to get > > the data the way you want it from the database. This is what the > > database is good at. > > Also, have you thought of actually storing a datetime for the post, > > instead of doing all this crazy number stuff? Then you could use the > > strftime method on the datetime to print out the dates nicely. Much > > more elegant. > > > Julian. > > > ---------------------------------------------- > > Learn:http://sensei.zenunit.com/ > > Last updated 20-May-09 (Rails, Basic Unix) > > Blog:http://random8.zenunit.com/ > > Twitter:http://twitter.com/random8r > > > > <% (posts.first["year"]..posts.last["year"]).to_a.reverse.each do | > > > year| > > > %> > > > <p><%= year %></p> > > > <% posts_by_year = posts.select{|hash| hash["year"] == year} %> > > > <ul class="nostyle"> > > > <% (1..12).to_a.reverse.each do |index| %> > > > <% posts_by_month = posts_by_year.select{|hash| hash["month"] => > > ("%02d" % index)}%> > > > <% unless posts_by_month.length == 0 %> > > > <li><%= link_to "#{Date::MONTHNAMES[index]} #{year}", > > > month_path(year, ("%02d" % index)) %> (<%= posts_by_month.length > > > %>)</li> > > > <% end %> > > > <% end %> > > > </ul> > > > <% end %> > > > > It''s working well. It''s all still stuck in the view, but oh well. If > > > anyone knows a better place, let me know. > > > -- > > > Posted viahttp://www.ruby-forum.com/.