开发者

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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜