Jonathan Clark
Jonathan Clark

Reputation: 20538

How to get scope with polymorphic association

I am building a Rails 5 app and in this app I got two models. First one is called Timeoff and second one is called Approval.

I want to get all Timeoff objects that got no approvals.

The time off model

class Timeoff < ApplicationRecord

  scope :not_approved, -> { self.approvals.size > 0 }

  has_many :approvals, as: :approvable, dependent: :destroy

end

The Approval model

class Approval < ApplicationRecord

  belongs_to :approvable, polymorphic: true

end

I am calling it like this

Timeoff.not_approved

I get the error

NoMethodError: undefined method `approvals' for #<Class:0x007f9698587830>

Upvotes: 1

Views: 951

Answers (1)

tegon
tegon

Reputation: 538

You're trying to call approvals in the class context, but it actually belongs to an instance of Timeoff. For example:

Timeoff.approvals # doesn't work
Timeoff.first.approvals # works

That's why you get the undefined method error. But I think you want a database query here. You could go two ways - that I know of:

Make two queries: find the timeoffs that have approvals and then query for the other ones using NOT IN

timeoff_ids = Approval.where(approvable_type: 'Timeoff').pluck(:approvable_id)
Timeoff.where.not(id: timeoff_ids)

This may get really slow if your tables are big. Or you could do a join on the approvals table and filter to where the id is null:

Timeoff.joins("LEFT JOIN approvals ON timeoffs.id = approvals.approvable_id AND approvals.approvable_type = 'Timeoff'").where("approvals.id IS NULL")

This should also work, and may be faster - but you should measure with your own data to be sure.

Also, take a look at this question: How to select rows with no matching entry in another table? there is a complete explanation of the second query and some other ways to solve it.

Upvotes: 2

Related Questions