rails: Preload no longer associates record with owner when custom scope is passed
Steps to reproduce
ActiveRecord::Associations::Preloader.new.preload(records, :association, Association.scope)
Expected behavior
Preloaded records when using custom scope should be associated with owner
Actual behavior
Custom scope is loaded into hash #record_by_owner and not associated with owner.
This defeats the purpose of preloading the association with a custom scope.
System configuration
Rails version: 6.0.0rc1
Ruby version: 2.5.1
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Reactions: 11
- Comments: 18 (9 by maintainers)
Commits related to this issue
- Fix preloader to associate preloaded records by default Someone had relied on the behavior that preloading with a given scope, but the behavior has lost in #35496 to fix the minor bug that unloading ... — committed to kamipo/rails by kamipo 4 years ago
- Fix preloader to associate preloaded records by default Someone had relied on the behavior that preloading with a given scope, but the behavior has lost in #35496 to fix the minor bug that unloading ... — committed to madmax/rails by kamipo 4 years ago
- Fix preloader to associate preloaded records by default Someone had relied on the behavior that preloading with a given scope, but the behavior has lost in #35496 to fix the minor bug that unloading ... — committed to madmax/rails by kamipo 4 years ago
While I agree that it’s undocumented, there’s precedent of other users using it. For example, in this blog post. http://aserafin.pl/2017/09/12/preloading-associations-with-dynamic-condition-in-rails/
Another common use case would be to check whether a user has ‘liked’ a collection of resources. To load all the resource
Resource.all
To check whether a current user a liked said resource, it’s inefficient to doResource.all.includes(:likes)
, as it will load all likes into memory. Previously, we could use the custom scope to load just the current user’s likes.The current change will cause all applications that were previously using this functionality to break silently. If a custom scope will never be useful, perhaps the change should remove that option from the method and throw an explicit error.
Here is a possible workaround:
Or for those struggling with
graphql-batch
here is the updated association_loader.rb.Yeah, Preloader is not a part of the public API. So, I wouldn’t expect Core Team consider it a bug as well. Should we made Preloader public? That’s a different question (and I think, the answer should be “Nope”).
The blog post i linked has an example
This would allow you to preload only the necessary prices
Another example would be
Well, I can’t agree here. It seems that the association preloader became way too widely adopted to ignore its importance for various gems and projects.
It is hard to think about it as “private” when it was actually “documented” in various blog posts throughout the last years and is being used in many gems (including quite popular ones).
@NoNonsenseSolutions that will be impossible now. This functionality is not officially documented, so I am pretty sure we can not consider this a bug.
What is your use case on having a custom scope for association in particular context? This functionality breaks OOD because it makes association reader method to behave differently in different context, so I would prefer to find a different workaround for this type of functionality like defining additional association with custom scope or modify association at the instance level.
This also breaks functionality of gems like
graphql-preload
that uses this feature to allow to customize preloading of associations.For example I’m using custom scopes to ensure custom ordering of associated records.
Which will execute something like that if and only if a client will ask for user’s accounts in GraphQL query:
See https://github.com/ConsultingMD/graphql-preload/issues/27
have some serious issue here too because of that (especially using / trying to port graphql-preload for rails 6), a gem which is pretty useful and still used by some people
I’m unsure if I did understand the problem correctly, but why are we using internal Rails mechanisms again?
At least for @coorasse’s gist, there’s a straight-forward workaround using the public API: https://gist.github.com/coorasse/63fc88b58e13a2eae27e27d95e82db05#gistcomment-3216808