[LRUG] Headache with AR & fields_for

Tim Ruffles tim at cothink.co.uk
Tue Jul 27 10:09:14 PDT 2010


Hi, I'm having a problem with AR - I've only been playing with Rails  
for 3 months or so, perhaps I'm missing something on this :)

I'm really happy with AR and fields_for, it cut out a load of cruft.  
But today I realised my form wasn't working as expected. When I  
perform query #1 although the filtering for each step's todos is  
already specified, a new query is performed when I call step.todos if  
no Todo was returned initially. with_todos_for is a named scope that  
uses SQL on the joins to add stuff to the ON clause.

I had a long look at the Rails API and can't see anything that could  
help (tried freezing, disconnecting. I guess I need to mark the  
'todos' association as already searched for, so no further query is  
made). Essentially as no Todos are returned for the Steps, they  
requery without any user filter, pulling in all of a Step's todos  
unfiltered.

(Query #1) ruby-1.8.7-p174 > the_recipe =  
Recipe.with_todos_for(User.find(45)).find(1)
   Recipe Load (9.0ms)   SELECT "recipes".* FROM "recipes" LEFT OUTER  
JOIN steps ON recipes.id = steps.recipe_id LEFT OUTER JOIN todos ON  
todos.step_id = steps.id AND todos.user_id = 45 WHERE ("recipes"."id"  
= 1)

ruby-1.8.7-p174 > the_recipe.steps
   Step Load (4.5ms)   SELECT * FROM "steps" WHERE ("steps".recipe_id  
= 1)

ruby-1.8.7-p174 > the_recipe.steps.first.todos
   Todo Load (3.4ms)   SELECT * FROM "todos" WHERE ("todos".step_id =  
30)
  => [#<Todo id: 30, step_id: 30, user_id: 43, completed_date:  
"2010-07-27 16:43:30", reminder_date: nil, created_at: "2010-07-27  
16:43:22", updated_at: "2010-07-27 16:43:51">]


As you can see, we now have a Todo with a user_id 43, not the 45 that  
the initial query requested - it's worse as in the real version it  
pulls in everybody's todos.


This second load is triggered by the following line in my view:

- form_for recipe, :url => {:action => :update} do |recipe_form|
   ....
     - recipe_form.fields_for :steps do |steps_form|
       - step = steps_form.object
       - step.todos.create :user => current_user  if step.todos.empty?  
# this triggers query #3, and pulls in all todos, causing hundreds of  
iterations for the following fields_for:
       %li
         - steps_form.fields_for :todos do |f|
           ....

Anyone got any ideas? It'd be great to keep the controller as simple  
as fields_for normally allows!

Thanks,

Tim Ruffles



More information about the Chat mailing list