mr_muscle
mr_muscle

Reputation: 2900

Ruby includes with scope

In my Rails 6 app I've got model Journey with defined active scope:

class Journey < ApplicationRecord
  has_many :users, through: :journey_progresses
  has_many :journey_progresses, dependent: :destroy

  scope :active, -> { where(is_deleted: false) }
end

In my endpoint I want to show user journey_progress and for serializer journey itself. To do so I'm using below query which works well

      def query
        current_user.journey_progresses.includes(:journey)
      end

How to modify above query to show only active journeys? I tried to just add where to be like current_user.journey_progresses.includes(:journey).where(is_deleted: false) but I'm getting an error:

Caused by PG::UndefinedColumn: ERROR:  column journey_progresses.is_deleted does not exist

According to this answer I tried with current_user.journey_progresses.includes(:journey).where(journey: {is_deleted: false} ) but I'm getting another error:

Caused by PG::UndefinedTable: ERROR:  missing FROM-clause entry for table "journey"

What is the right syntax for that sort of action?

Upvotes: 3

Views: 2133

Answers (4)

claasz
claasz

Reputation: 2134

You are misusing includes here. includes is only for optimization, to reduce number of queries going to the database.

Since you want to filter rows based on values in journey, you need to join it:

current_user.journey_progresses.joins(:journey)

As a general advice for these kind of problems I would recommend looking at the Rails log to see what SQL is generated at the end. Dealing with multiple has_many, scope, include, etc. can become confusing quickly.

UPDATE

You can actually combine joins with includes like this:

Foo.includes(:bars).where('bars.name = ?', 'example').references(:bars)

See https://api.rubyonrails.org/classes/ActiveRecord/QueryMethods.html#method-i-includes

Upvotes: 2

Aarthi
Aarthi

Reputation: 1521

current_user.journey_progresses.includes(:journey).where(journey: {is_deleted: false} )

The above syntax is available from Rails 5.

You could try below possible ways,

  1. Adding custom association

In JourneyProgress Model

class JourneyProgress < ApplicationRecord
    belongs_to :active_journey, -> { where(is_deleted: false) }, class_name: 'Journey'
end

and include the association while querying.

    current_user.journey_progresses.includes(:active_journey)

  1. Adding default scope - But this scope will always be applied on querying from that model(https://apidock.com/rails/ActiveRecord/Base/default_scope/class)

default_scope -> { where(is_deleted: false) }

Upvotes: 2

SteveTurczyn
SteveTurczyn

Reputation: 36860

You can merge a scope of a joined model.

current_user.journey_progresses.joins(:journey).merge(Journey.active)

This is better than testing for the is_deleted boolean because in future, if the definition of what makes a journey active changes, you won't need to modify the above line.

Upvotes: 6

Kumar
Kumar

Reputation: 3126

Can you try the following?

current_user
  .journey_progresses.
  .joins(:journey)
  .where("journeys.is_deleted = ?", false)

Upvotes: 2

Related Questions