2

I have 2 models: Sophead and SopheadIssue.

SopheadIssue belongs to Sophead,

Sophead has many SopheadIssues (optional).

I want to create a scope on the Sophead model for Sopheads that match EITHER of the 2 conditions:

  1. The Sophead has no SopheadIssues at all
  2. The Sophead has no SopheadIssues with a SopheadIssue attribute of (active=true).

At the moment I have tried the following:

scope :no_issue, -> { joins(:sophead_issues).where.not("active = ?", true) }

But this isn't working as it is missing Sopheads without any SopheadIssues.

Any help much appreciated.
Thanks very much!

daveanderson88
  • 327
  • 1
  • 4
  • 12

2 Answers2

7

The problem is that joins is INNER JOIN and it filters out the sophead without sophead_issues. You need to use left_joins here:

scope :no_issue, -> { left_joins(:sophead_issues).where("sophead_issues.active != ? OR sophead_issues.sophead_id IS NULL", true) }
Igor Drozdov
  • 14,690
  • 5
  • 37
  • 53
0

I believe the following should work, though I'm unable to test and confirm this at the moment:

scope :active, -> { joins(:sophead_issues).where.not(sophead_issues: { active: true }) }

scope :no_associations, -> { joins(:sophead_issues).where.not(sophead_issues: { sophead_id: nil }) }

scope :no_issue, -> { active.no_associations }) }

I've broken this down into a couple of queries for my own sanity, though were I able to test, I imagine you should be able to combine them using or and not. However, I'm not certain how to implement this without testing it out a little.

Basically, the active scope is self explanatory: you find the sophead_issues with active set to true.

The no_associations your sopheads without an association. More on that here if you're interested.

Finally, no_issue combines the two methods, returning your collection.

You could refactor this into a class method, or two class methods that chain to form the scope, depending on your preference.

Edit: or use @IgorDrozdov's approach, that's much cleaner :) (Unless you're < Rails 5, where left_joins was introduced.)

SRack
  • 11,495
  • 5
  • 47
  • 60