I am working on a Rails 3 application, and am having some trouble with my user/edit view. In this application, Users can subscribe to Categories. Users and Categories have a has_and_belongs_to_many relationship through a join table (categories_users).
In my user/edit view, I need to display a list of checkboxes for the various categories so the user can edit his subscriptions. I am using the following code to achieve this:
<div id="category-checklist">
<% Category.roots.order('name ASC').each do |category| %>
<ul>
<li class="parent <%= category.name %>">
<%= check_box_tag "user[category_ids][]", category.id, @user.categories.include?(category) %> <span><%= category.name %></span>
</li>
<ul id="children-<%= category.id %>" class="children-list">
<% category.children.order('name ASC').each do |child| %>
<li class="child <%= child.name %>">
<%= check_box_tag "user[category_ids][]", child.id, @user.categories.include?(child) %> <span><%= child.name %></span>
</li>
<% end %>
</ul>
</ul>
<% end %>
</div>
The Category model uses the ancestry gem for hierarchy, which is there the roots
and children
methods come from. Roots returns all the parent categories, and children returns all of the sub-categories for a given parent.
The really problematic part of this code seems to be @user.categories.include?(child)
which is running 81 separate queries (one for each child category). This line is used to check if the category is currently subscribed to by the user -- if so, the checkbox is marked checked. Is there a better/faster way to accomplish this?
For reference, here is the join table's schema definition - I tried to put an index on the foreign keys thinking it would speed things up, but this is still very slow compared to the rest of my app.
create_table "categories_users", :id => false, :force => true do |t|
t.integer "category_id"
t.integer "user_id"
end
add_index "categories_users", ["category_id"], :name => "index_categories_users_on_category_id"
add_index "categories_users", ["user_id"], :name => "index_categories_users_on_user_id"
Thanks for any help!