Rails, is it ok to look up objects in views?
A controller points to a view. In that view is it acceptable to find objects <% @XXX = XXXX.where(..... %>
or is that bad?
Trying to work through performance issues whi开发者_Python百科ch is why I ask. Thanks
Putting query logic in the model has more to do with maintainability then it does with performance. Since most of the ActiveRecord/ARel logic deals with lightweight relation objects that only trigger the actual query based on certain methods, generally those provided via Enumerable (each/map/inject/all/first), which are usually called from the view anyway, the actual query gets triggered in the view, and not anywhere else.
Here's an example of the difference between limit(3) and first(3) from an app I'm working on atm.
ruby-1.9.2-p180 :018 > PressRelease.limit(3).is_a? ActiveRecord::Relation
=> true
ruby-1.9.2-p180 :019 > PressRelease.first(3).is_a? ActiveRecord::Relation
PressRelease Load (2.8ms) SELECT "press_releases".* FROM "press_releases" ORDER BY published_at DESC
=> false
As you can see, limit does not actually trigger a query, where first does.
When it comes to performance you are usually trying to ensure that your queries are not executed in your controller/model so that you can wrap them in a cache block within your view, thus eliminating that query from most requests. In this case you really want to make sure your not executing the query in your controller by calling any of the Enumerable methods.
A quick example of a blog that lists the last 10 blog posts on the home page that is setup with caching might look like this.
# app/controllers/posts_controller.rb
class PostsController < ApplicationController
def index
# Something like this would trigger the query at this point and should be
# avoided in the controller
# @posts = Post.first(10)
# So @posts here will be the Relation returned from the last_ten scope, not
# an array
@posts = Post.last_ten
end
...
end
# app/models/post.rb
class Post < ActiveRecord::Base
# Will return an ActiveRecord::Relation
scope :last_ten, order('created_at DESC').limit(10)
end
# app/views/posts/index.html.erb
<ul>
# The query will actually trigger within the cache block on the call to each,
# preventing the query from running each time and also reducing the template
# rendering within the cache block.
<%= cache(posts_cache_key) do %>
<% @posts.each do |post| %>
..
<% end %>
<% end %>
</ul>
For clarity, all this would be the exact same as doing
# app/views/posts/index.html.erb
<ul>
<%= cache(posts_cache_key) do %>
<% Post.order('created_at DESC').limit(10).each do |post| %>
...
<% end %>
<% end %>
</ul>
Except that if you now want to modify the logic for how it pulls the query, say you wanted to add something like where(:visible => true).where('published_at' <= Time.now)
your jumping into your view instead of making changes in the model where the logic should be. Performance wise the difference is insignificant, maintenance-wise the latter turns into a nitemare rather quickly.
精彩评论