I am having trouble preloading a belongs_to association.
The model in question has following association
class Order < ActiveRecord::Base
belongs_to :user_name, -> { select(:id, :name) }, class_name: "User", :foreign_key => 'user_id'
end
The above association works fine and resolves as expected.
I want to return the user.id and user.name along with order.
How do I preload the association user_name.
I did try Order.includes(:user_name), which did not work as expected.
I would change association just to:
class Order < ActiveRecord::Base
belongs_to :user
end
and select users.id and users.name elsewhere. It makes your code more flexible.
How do I preload the association
ActiveRecord provides a few ways for that:
Preload. Loads the association data in a separate query. Since
preload always generates two sql you can not use preloaded table in
where condition.
Includes. By default loads the association data in
a separate query just like preload. But with additional references
call it switches from using two separate queries to creating a
single LEFT OUTER JOIN. So with that you can use association tables in where condition.
Eager load. Loads association in a single query using LEFT OUTER JOIN.
Here is some useful articles:
Preload, Eagerload, Includes and Joins
Making sense of ActiveRecord joins, includes, preload, and eager_load
Try adding a scope like below
scope : user_name_id, -> { joins(:user_name).select( 'user_names.id, user_names.name' ).collect{|c| [c.id, c.name]} }
Related
I know there're many questions about some of these topics, but I didn't find one covering all aspects.
Consider User, Activity and Like models. When I query an activity I would like to eager load the first Like for each activity in the collection without making N+1 queries and without loading more than necessary records. My code looks something like this:
class User < ActiveRecord::Base
has_many :likes, as: :liker
end
class Activity < ActiveRecord::Base
has_many :likes
has_one :first_like, -> { order(created_at: :asc) }, class_name: "Like"
end
class Like < ActiveRecord::Base
belongs_to :activity
belongs_to :liker, polymorphic: true
end
I made a comprehensive gist to test different loading strategies and methods: https://gist.github.com/thisismydesign/b08ba0ee3c1862ef87effe0e25386267
Strategies: N+1 queries, left outer join, single extra query
Methods: eager_load, includes, includes & references, includes & preload (these will result in either left outer join or single extra query)
Here're the problems I discovered:
Left outer join doesn't respect order(created_at: :asc) in the association scope nor default_scope { order(created_at: :asc) } (see: rails issue). It does respect explicit ordering i.e. .order("likes.created_at asc").
Left outer join should nevertheless be avoided because it "could result in many rows that contain redundant data and it performs poorly at scale" (see: apidoc rubydoc, rails api). This is a real issue with lots of data even with indexed searches on both sides.
Single extra query will create a query without limit, potentially fetching huge amounts of data (see: rails issue)
Adding an explicit limit(1) to the association in hope of constraining the single extra query will break things
The preferred method would be a single extra query that only queries the required records. All in all, I couldn't find a native solution with Rails. Is there any?
In my question, I'm looking for a native way using Rails. However, here's a solution using SQL and virtual attributes:
class Activity < ApplicationRecord
has_one :first_like, class_name: "Like", primary_key: :first_like_id, foreign_key: :id
scope :with_first_like, lambda {
select(
"activities.*,
(
SELECT like_id as first_like_id
FROM likes
WHERE activity_id = activities.id
ORDER BY created_at ASC
LIMIT 1
)"
)
}
end
Activity.with_first_like.includes(:first_like)
class A < ActiveRecord::Base
has_many :Bs
has_many :Cs
...
end
I wish to load all of B's and C's whenever I do a query on A, say A.where(name: :abc), with a single query, instead of multiple calls to database.
I don't wish to specify .includes for every query I run. How do I specify eager loading in the model itself?
I looked many similar question and tried do this but it does not work:
default_scope :include => [:Bs, :Cs]
default_scope { includes(:Bs, :Cs) } should do it.
As far as I know the scope takes a block as argument not a hash of options. I just tried it in the rails console and seems to work.
I have a model Gallery that has_many :images, as: :imageable, dependent: :destroy and Image model that belongs_to :imageable, polymorphic: true. Single Table Inheritance is being used here. Now I want to fetch and hide the Galleries which don't have images associated to it. How to go about it?
If you simply want to most effectively fetch all galleries with an empty association, you could use this scope:
# in Gallery.rb
scope :no_images, -> {
joins('LEFT OUTER JOIN images ON images.imageable_id = galleries.id).
group('galleries.id').
having('count(imageable_id) = 0') }
Running Gallery.no_images would return all Galleryobjects with no associated images.
Please note that the docs have the following to say about using polymorphic associations with Single Table Inheritance:
Using polymorphic associations in combination with single table
inheritance (STI) is a little tricky. In order for the associations to
work as expected, ensure that you store the base model for the STI
models in the type column of the polymorphic association. To continue
with the asset example above, suppose there are guest posts and member
posts that use the posts table for STI. In this case, there must be a
type column in the posts table.
So ensure that you have properly set the type column to the correct value (`"Image" in your case).
Try this
Gallery.all.each do |gallery|
galleries << gallery if (gallery.images.empty?)
end
I'm new to Rails, and while writing Active Record queries, I notice that all columns of all associated tables are being retrieved. I would like to tell Active Record which fields from which tables ought to be retrieved. How would go about doing that?
My models and their associations are as follows:
class User < ActiveRecord::Base
has_one :profile
has_many :comments
has_many :posts
end
class Profile < ActiveRecord::Base
belongs_to :user
end
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :post
end
class Post < ActiveRecord::Base
belongs_to :user
has_many :comments
end
I'm following the Rails Edge Guides, and when I try to use select("users.id, profiles.first_name, profiles.last_name, comments.comment") to specify the field lists, I get a deprecation warning on the Rails console (and the SQL query that is run is a LEFT OUTER JOIN of all tables involved, but it still includes all columns):
DEPRECATION WARNING: It looks like you are eager loading table(s) (one of: users, posts) that are referenced in a string SQL snippet. For example:
Post.includes(:comments).where("comments.title = 'foo'")
Currently, Active Record recognizes the table in the string, and knows to JOIN the comments table to the query, rather than loading comments in a separate query. However, doing this without writing a full-blown SQL parser is inherently flawed. Since we don't want to write an SQL parser, we are removing this functionality. From now on, you must explicitly tell Active Record when you are referencing a table from a string:
Post.includes(:comments).where("comments.title = 'foo'").references(:comments)
If you don't rely on implicit join references you can disable the feature entirely by setting `config.active_record.disable_implicit_join_references = true`. (called from irb_binding at (irb):34)
Check if following work for you
Class User < ActivcRecord::Base
default_scope select("column1, column2, column3")
end
Buried deep inside the Rails Edge Guides for Active Record Query Interface, I found the answer. The trick is to use scopes for the particular association type where you want to restrict the retrieved fields.
Quoted directly from the guide:
4.1.3 Scopes for belongs_to
There may be times when you wish to customize the query used by belongs_to. Such customizations can be achieved via a scope block. For example:
class Order < ActiveRecord::Base
belongs_to :customer, -> { where active: true },
dependent: :destroy
end
You can use any of the standard querying methods inside the scope block.
So, adding a select method to the above scope, with the list of fields you want retrieved will do the trick.
Here is my model:
class User < ActiveRecord::Base
has_many :activities
has_many :requests
class Activity < ActiveRecord::Base
belongs_to :user
belongs_to :object, :polymorphic => true
I want to get all the users activities and display them
Activity.where(:user_id => current_user.id).include(:object)
the problem is that I can't eager load the object model because it's polymorphic
How do I overcome this problem?
Eager loading is supported with polymorphic associations. You will need to do something along the following lines:
Activity.find(:all, :include => :objectable, :conditions => {:user_id => current_user.id})
Although you need to make sure that you have defined the polymorphic relationship correctly on the associated models.
For further help refer to:
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#label-Eager+loading+of+associations
The polymorphic part is at the end of "Eager loading of Associations" section.
As #Wahaj says, eager loading only works with :includes and not :join.
Here's the explanation from the docs:
Address.find(:all, :include => :addressable)
This will execute one query to load the addresses and load the addressables with one query per addressable type. For example if all the addressables are either of class Person or Company then a total of 3 queries will be executed. The list of addressable types to load is determined on the back of the addresses loaded. This is not supported if Active Record has to fallback to the previous implementation of eager loading and will raise ActiveRecord::EagerLoadPolymorphicError. The reason is that the parent model’s type is a column value so its corresponding table name cannot be put in the FROM/JOIN clauses of that query.
I think this is what you're after:
current_user.activities.includes(:object)
As the docs say, there will be an extra query for each association. I'm not sure, but you may need to define an association the other direction for rails to know which AR models to search, eg:
class Post < ActiveRecord::Base
has_many :activities, :as => :object
end
If you're still getting an error, you might be on an earlier rails version which hadn't yet implemented this.
Its not possible to have eager loading to the Polymorphic relationship ... but u can do it for one polymorphic type like if u r having two polymorphic_type then filter the records on that type and then make eager loading it will work then .... not the perfect eager loading but partial eager loading