Fragment caching and not accessing model from view

I am using fragment caching to render a select box where the contents
of the select box comes from a lengthy db query. A simplified example
showing the problem is
<% cache 'select_box' do %>
<%= f.collection_select :variety_id, @varieties, :id, :name %>
<% end %>
where @varieties is setup in the controller. Unfortunately this does
not achieve the desired result as the query is run even when the
select is picked up from the cache.

To get the full benefit I have to remove @varieties = Variety.all from
the controller and use
<% cache 'select_box' do %>
<%= f.collection_select :variety_id, Variety.all, :id, :name %>
<% end %>
but this breaks the rule that one should not access the model from the
view. Is there a good solution to this problem?

Colin

In the controller, you can check for the presence of the cached fragment using the read_fragment method. Then, only run the database query if the fragment isn't cached yet.

Chris

That's a great idea, in fact it is probably
fragment_exists?('select_box') that I want to use. I will give it a
go.

Thanks

Colin

I am using fragment caching to render a select box where the contents

of the select box comes from a lengthy db query. A simplified example

showing the problem is

<% cache ‘select_box’ do %>

<%= f.collection_select :variety_id, @varieties, :id, :name %>

<% end %>

where @varieties is setup in the controller. Unfortunately this does

not achieve the desired result as the query is run even when the

select is picked up from the cache.

are you just using .all or a complex query? Because if you’re using a complex

query and you’re using rails3, then just remove the .all in the complex query

in the controller so that it won’t run the query. I think it just saves the

relation to the @varieties variable and won’t run the query until a method is

called on the variable such .each or .collect.

I am using fragment caching to render a select box where the contents

of the select box comes from a lengthy db query. A simplified example

showing the problem is

<% cache ‘select_box’ do %>

<%= f.collection_select :variety_id, @varieties, :id, :name %>

<% end %>

where @varieties is setup in the controller. Unfortunately this does

not achieve the desired result as the query is run even when the

select is picked up from the cache.

To get the full benefit I have to remove @varieties = Variety.all from

the controller and use

<% cache ‘select_box’ do %>

<%= f.collection_select :variety_id, Variety.all, :id, :name %>

<% end %>

but this breaks the rule that one should not access the model from the

view. Is there a good solution to this problem?

Yes, you are right. One should not access the model from the view.

Create a method in the model, say

       def self.your_method_name
           your_query
       end

and then you can access the model in your view, using the method you just defined in the model.

I don't see how:

  <%= f.collection_select :variety_id, Variety.all, :id, :name %>

is different from :

  <%= f.collection_select :variety_id, Variety.your_method_name, :id, :name %>

?:-/

If you say in one breath "don't access models from the view" and then
in the next create a model class method... how's that different from
using the ".all" class method?

(btw I agree that one shouldn't access the model from the view - I'm
just querying that your suggestion may not remove this MVC-breaking
practice in this instance)

One take on this was interlock ( https://github.com/fauna/interlock )
although i haven't used it personally (and looks like it hasn't been
touched in a while)

Fred

Just to confirm that, in the controller,

    @varieties = Variety.all if !fragment_exist?( 'select_box' )

does the job.

Colin

I had noticed that some queries don't get run till the collection is
used, whereas others seem to run immediately. I did not find
documented exactly how to tell in advance which will get run and which
won't. I wonder whether using a named scope would guarantee delayed
operation.

Colin

(named) scopes are lazily evaluated.

Fred

hi fred,

Ya i can get your point,but in rails we will use scope instead of named_scope.If i try rake task separately means its working.

hi,
I also have a query how to integrate rake task with cronjobs any idea how to use with example.

Anu

OK, I provided a scope
scope :all_varieties
with no parameters.

Then in the controller
@varieties = Variety.all_varieties
and have confirmed that if the fragment is already cached so
@varieties is not used in the view, then the query is not run, so no
need test whether the fragment exists when setting up @varieties.

This is the best solution I think.

Thanks again

Colin

Colin Law wrote in post #986452:

OK, I provided a scope
scope :all_varieties
with no parameters.

Then in the controller
@varieties = Variety.all_varieties
and have confirmed that if the fragment is already cached so
@varieties is not used in the view, then the query is not run, so no
need test whether the fragment exists when setting up @varieties.

This is the best solution I think.

Thanks again

Colin

Ack... obfuscated logic. If you are trying to not make the call when
the cache fragment already exists, then don't do so.

@varieties = Variety.all unless fragment_exist?( 'select_box' )

Don't squirrel it away behind a scope which does nothing except let you
defer the decision about executing the DB read and depend on a behavior
of AR which is determined when the view is being rendered...

Just a personal opinion. What works for you, works for you.

+1

Colin Law wrote in post #986452:

OK, I provided a scope
scope :all_varieties
with no parameters.

Then in the controller
@varieties = Variety.all_varieties
and have confirmed that if the fragment is already cached so
@varieties is not used in the view, then the query is not run, so no
need test whether the fragment exists when setting up @varieties.

This is the best solution I think.

Thanks again

Colin

Ack... obfuscated logic. If you are trying to not make the call when
the cache fragment already exists, then don't do so.

Sorry I am not sure what you are saying is best, are you suggesting I
*should* use fragment_exist? or I should not?

Colin

hi,
I also have a query how to integrate rake task with cronjobs any idea how
to use with example.

What has that got to do with the subject of this thread (fragment caching)?