I have the following models:
class User < ApplicationRecord
has_one :card, as: :cardable
scope :active, -> { where(active: true) }
end
class Organisation < ApplicationRecord
has_one :card, as: :cardable
scope :active, -> { where(active: true) }
end
class Card < ApplicationRecord
belongs_to :cardable, polymorphic: true
end
I want to find all the cards whose associated User or Organisation is active.
I thought the following would work:
Card.includes(:cardable).where(cardable: {active: true})
But this throws an error:
ActiveRecord::EagerLoadPolymorphicError: Cannot eagerly load the polymorphic association :cardable
Is what I'm trying to do even possible with ActiveRecord?
I've looked other questions with a similar title, but I am not sure the scenarios are similar enough to this one.
I think this should work:
cardable_ids = User.active.pluck(:id) + Organisation.active.pluck(:id)
Card.where(cardable_id: cardable_ids)
Related
I want to be able to find unpopulated hives, but don't find any solution.
Can you help me please ?
The goal is to be able to do Hive.unpopulated
The main problem is the most_recent, butins ok for me to work with a raw SQL, but I don't find the right query.
Here are my classes :
class Hive < ApplicationRecord
has_many :moves, dependent: :destroy
has_many :yards, through: :moves
has_many :populations, -> { where(:most_recent => true) }
has_many :colonies, through: :populations
validates :name, uniqueness: true
def hive_with_colony
"#{name} (colony #{if self.colonies.count > 0 then self.colonies.last.id end})"
end
def self.populated
Hive.joins(:populations)
end
def self.unpopulated
end
end
class Population < ApplicationRecord
belongs_to :hive
belongs_to :colony
after_create :mark_most_recent
before_create :mark_end
class Colony < ApplicationRecord
has_many :populations, -> { where(:most_recent => true) }
has_many :hives, through: :populations
has_many :visits
has_many :varroas
has_many :most_recents_populations, -> { where(:most_recent => true) }, :class_name => 'Population'
scope :last_population_completed, -> { joins(:populations).where('populations.most_recent=?', true)}
I think you can do a simple query to select Hives which are not in populated list, so:
def self.unpopulated
where.not(id: populated.select(:id))
end
Another option is a LEFT OUTER JOIN and picking the lines that have no population id set on the right side.
def self.unpopulated
left_outer_joins(:populations).where(populations: { id: nil })
end
It depends on your data if Thanh's version (which compares a potentially huge list of ids) or this version (which makes a sightly more complex join but doesn't need to compare against a list of ids) is more performant.
I have model with polymorhphic reference to two other models. I've also included distinct references per this article eager load polymorphic so I can still do model-specific queries as part of my .where clause. My queries work so I can search for scores doing Score.where(athlete: {foo}), however, when I try to do a .create, I get an error because the distinct reference alias seems to be blinding Rails of my polymorphic reference during validation.
Given that athletes can compete individually and as part of a team:
class Athlete < ApplicationRecord
has_many :scores, as: :scoreable, dependent: :destroy
end
class Team < ApplicationRecord
has_many :scores, as: :scoreable, dependent: :destroy
end
class Score < ApplicationRecord
belongs_to :scoreable, polymorphic: true
belongs_to :athlete, -> { where(scores: {scoreable_type: 'Athlete'}) }, foreign_key: 'scoreable_id'
belongs_to :team, -> { where(scores: {scoreable_type: 'Team'}) }, foreign_key: 'scoreable_id'
def athlete
return unless scoreable_type == "Athlete"
super
end
def team
return unless scoreable_type == "Team"
super
end
end
When I try to do:
Athlete.first.scores.create(score: 5)
...or...
Score.create(score: 5, scoreable_id: Athlete.first.id, scoreable_type: "Athlete")
I get the error:
ActiveRecord::StatementInvalid (SQLite3::SQLException: no such column: scores.scoreable_type
Thanks!
#blazpie, using your scoping suggestion worked for me.
"those scoped belongs_to can be easily substituted by scopes in Score: scope :for_teams, -> { where(scorable_type: 'Team') }
I am trying to build an active record query using through table associations. Here are my models:
Event.rb:
has_many :event_keywords
User.rb:
has_many :user_keywords
Keyword.rb:
has_many :event_keywords
has_many :user_keywords
EventKeyword.rb:
belongs_to :event
belongs_to :keyword
UserKeyword.rb:
belongs_to :user
belongs_to :keyword
I am trying to build an Event scope that takes a user_id as a param and returns all the Events with shared keywords. This was my attempt but it's not recognizing the user_keywords association:
scope :with_keywords_in_common, ->(user_id) {
joins(:event_keywords).joins(:user_keywords)
.where("user_keywords.user_id = ?", user_id)
.where("event_keywords.keyword_id = user_keywords.keyword_id")
}
How do I resolve this?
Something like this might work. 2-step process. First, get all user's keywords. Then find all events with the same keyword.
scope :with_keywords_in_common, ->(user) {
joins(:event_keywords).
where("event_keywords.keyword_id" => user.user_keywords.pluck(:id))
}
The database seems to be overkill here and firstly I'd simplify by making keywords polymorphic, this would get rid of 2 of your tables here (event_keywords, and user_keywords).
Your associations would then look like this:
# Event.rb:
has_many :keywords, as: keywordable
# User.rb:
has_many :keywords, as: keywordable
# Keyword.rb:
belongs_to :keywordable, polymorphic: true
And finally, your scope:
scope :with_keywords_in_common, -> (user_id) do
joins(:keywords)
.where('keywords.keywordable_type = User AND keywords.word IN (?)', keywords.pluck(:name))
end
Is there any way to prepend to the existing order section of an active record query?
I the following associations defined on my Location model:
class Location < ActiveRecord::Base
has_many :location_parking_locations, -> { by_votes }
has_many :parking_locations, through: :location_parking_locations
end
In the LocationParkingLocation model, the by_votes scope is defined:
class LocationParkingLocation < ActiveRecord::Base
belongs_to :location
belongs_to :parking_location
scope :by_votes, -> { order("upvotes - downvotes ASC, upvotes + downvotes DESC, id ASC") }
end
I would like to add a scope to the ParkingLocation model that adds an additional scope to the query, but I want that scope to be prepended to the existing order section of the query. The scope looks like this:
class ParkingLocation < ActiveRecord::Base
has_many :location_parking_locations
has_many :locations, through: :location_parking_locations
scope :with_pending, -> { order(creation_pending: :desc) }
end
My hope is to call location.parking_locations.with_pending, and get back a collection of parking_locations, ordered by votes, but with any pending parking locations at the beginning of the collection. Is this possible?
Since a scope is a lambda, you can use arguments, for example:
scope :your_scope, -> (sort_order = :desc) { order(name: sort_order) }
Just set it up as you need and call it with a (possibly optional) argument:
your_model.your_scope(:desc)
I'm trying to return records where the association is either present or not:
I tried these scopes:
class Booking < ActiveRecord::Base
has_one :availability
scope :with_availability, -> {where{availability.not_eq nil}}
scope :without_availability, -> {where{availability.eq nil}}
end
Try this:
class Booking < ActiveRecord::Base
has_one :availability
scope :with_availability, -> { joins{availability} }
scope :without_availability, -> { joins{availability.outer}.where{availability.id.eq nil} }
end
Use instance methods instead
def with_availability
availability.present?
end
def without_availability
availability.blank?
end
I know there is better way but this should work as well:
class Booking < ActiveRecord::Base
has_one :availability
scope :with_availability, -> {where(id: Availability.pluck(:booking_id))}
scope :without_availability, -> {where.not(id: Availability.pluck(:booking_id))}
end
Also I tried to reproduce the solution by the link below but I didn't manage to do this (but it could be helpful):
Rails - has_one relationship : scopes for associated and non-associated objects