Note that there are some explanatory texts on larger screens.

plurals
  1. POUsing rails active_record 'includes' changes results of has_many attribute accessor depending on what is included
    text
    copied!<p>I have a <code>Project</code> and <code>User</code> model, that are connected through a <code>Permission</code> model with a has_many through, polymorphic association. The Permission table also tracks a pending status. The relevant setup is as follows:</p> <pre><code>class Project &lt; ActiveRecord::Base has_many :permissions, as: :permissionable has_many :contributors, through: :permissions, source: :user has_many :pending_contributors, through: :permissions, source: :user, conditions: '"permissions"."accepted" = false' ... end class Permission &lt; ActiveRecord::Base belongs_to :user belongs_to :permissionable, polymorphic: true ... end class User &lt; ActiveRecord::Base has_many :permissions has_many :projects, through: :permissions, source: :permissionable, source_type:'Project' ... end </code></pre> <p>I'm trying to access both the contributors and pending_contributors lists on projects without causing the N+1 query issue as I render all projects. The trouble is I can't use <code>includes(:contributors, :pending_contributors)</code> because whichever one of the two includes is mentioned first is the only one that works.</p> <p>For example in my rails console:</p> <pre><code>u = User.find(2) u.projects.includes(:contributors, :pending_contributors).each do |p| puts p.contributors.map(&amp;:id).inspect end </code></pre> <p>The result of this in my rails console is:</p> <pre><code>User Load (0.7ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1 [["id", 2]] Project Load (1.3ms) SELECT "projects".* FROM "projects" INNER JOIN "permissions" ON "projects"."id" = "permissions"."permissionable_id" WHERE "permissions"."user_id" = 2 AND "permissions"."permissionable_type" = 'Project' Permission Load (1.1ms) SELECT "permissions".* FROM "permissions" WHERE "permissions"."permissionable_type" = 'Project' AND "permissions"."permissionable_id" IN (64, 56, 54, 53, 51, 50, 61, 62, 63, 57, 65, 66, 48, 49, 67, 68, 69, 70, 71, 60, 72, 16, 14, 11) User Load (0.7ms) SELECT "users".* FROM "users" WHERE "users"."id" IN (2, 18, 20, 41, 40, 3, 17, 34, 37, 39, 43, 19, 22, 42) </code></pre> <p>with the first three results being:</p> <pre><code>[2, 18, 20, 2] [2] [41, 2, 40, 18] </code></pre> <p>Now swap the order of the includes as follows:</p> <pre><code>u = User.find(2) u.projects.includes(:pending_contributors, :contributors).each do |p| puts p.contributors.map(&amp;:id).inspect end </code></pre> <p>and suddenly the results change (yielding the results I expected) - note the permission load line, it now adds the :pending_contributors condition to that query:</p> <pre><code>User Load (0.8ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1 [["id", 2]] Project Load (1.1ms) SELECT "projects".* FROM "projects" INNER JOIN "permissions" ON "projects"."id" = "permissions"."permissionable_id" WHERE "permissions"."user_id" = 2 AND "permissions"."permissionable_type" = 'Project' Permission Load (1.4ms) SELECT "permissions".* FROM "permissions" WHERE "permissions"."permissionable_type" = 'Project' AND "permissions"."permissionable_id" IN (64, 56, 54, 53, 51, 50, 61, 62, 63, 57, 65, 66, 48, 49, 67, 68, 69, 70, 71, 60, 72, 16, 14, 11) AND ("permissions"."accepted" = false) User Load (0.8ms) SELECT "users".* FROM "users" WHERE "users"."id" IN (18, 19, 40, 3, 22, 43, 42, 41, 2, 20) </code></pre> <p>and the results are:</p> <pre><code>[20] [] [41, 40, 18] </code></pre> <p>If instead of accessing pending_contributors, I access contributors, I get the same issue in reverse. I'm pretty sure this is because both pending_contributors and contributors are using the same source, but I'm not sure why this is an issue.</p> <p>Is there a way to render all projects and access contributors, and pending_contributors for each project without causing an N+1 query?</p> <p>Note: I'm on Rails 3.2.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload