Skip to content

Commit 54a10ee

Browse files
authored
Make the :preload_order option in Ecto.Schema.has_many/3 not take precedence over custom preload queries (#4675)
1 parent e692f20 commit 54a10ee

File tree

4 files changed

+32
-0
lines changed

4 files changed

+32
-0
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog for v3.x
22

3+
## Unreleased
4+
5+
### Enhancements
6+
7+
* [Ecto.Repo] Preload custom queries with `order_by` now take precedence over `:preload_order`. The `:preload_order` option is now only applied when no custom query with ordering is provided.
8+
39
## v3.13.3 (2025-09-19)
410

511
### Enhancements

integration_test/cases/preload.exs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -695,6 +695,23 @@ defmodule Ecto.Integration.PreloadTest do
695695
assert [%{name: "foz"}, %{name: "baz"}] = post2.ordered_users_by_join_table
696696
end
697697

698+
test "custom query order_by overrides preload_order" do
699+
post = TestRepo.insert!(%Post{title: "1"})
700+
701+
TestRepo.insert!(%Comment{text: "2", post_id: post.id})
702+
TestRepo.insert!(%Comment{text: "1", post_id: post.id})
703+
TestRepo.insert!(%Comment{text: "3", post_id: post.id})
704+
705+
# Without custom query, preload_order (asc by text) should apply
706+
post = TestRepo.preload(post, :ordered_comments)
707+
assert [%{text: "1"}, %{text: "2"}, %{text: "3"}] = post.ordered_comments
708+
709+
# With custom query having order_by, it should override preload_order
710+
query = from(c in Comment, order_by: [desc: c.text])
711+
post = TestRepo.preload(post, [ordered_comments: query], force: true)
712+
assert [%{text: "3"}, %{text: "2"}, %{text: "1"}] = post.ordered_comments
713+
end
714+
698715
## Others
699716

700717
@tag :invalid_prefix

lib/ecto/repo/preloader.ex

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,11 @@ defmodule Ecto.Repo.Preloader do
483483

484484
defp add_preload_order([], query), do: query
485485

486+
defp add_preload_order(_order, %{order_bys: [_|_]} = query) do
487+
# Skip applying preload_order when query already has custom order_by clauses
488+
query
489+
end
490+
486491
defp add_preload_order(order, query) when is_list(order) do
487492
Ecto.Query.prepend_order_by(query, [q], ^order)
488493
end

lib/ecto/schema.ex

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -786,6 +786,8 @@ defmodule Ecto.Schema do
786786
For example, if you set `Post.has_many :comments, preload_order: [asc: :content]`,
787787
whenever the `:comments` associations is preloaded,
788788
the comments will be ordered by the `:content` field.
789+
Note that if you provide a custom query with its own `order_by` clause,
790+
the custom ordering will take precedence and the `:preload_order` will not be applied.
789791
See `Ecto.Query.order_by/3` to learn more about ordering expressions.
790792
791793
## Examples
@@ -1352,6 +1354,8 @@ defmodule Ecto.Schema do
13521354
It may be a keyword list/list of fields or an MFA tuple, such as `{Mod, fun, []}`.
13531355
Both cases must resolve to a valid `order_by` expression. See `Ecto.Query.order_by/3`
13541356
to learn more about ordering expressions.
1357+
Note that if you provide a custom query with its own `order_by` clause,
1358+
the custom ordering will take precedence and the `:preload_order` will not be applied.
13551359
See the [preload order](#many_to_many/3-preload-order) section below to learn how
13561360
this option can be utilized
13571361

0 commit comments

Comments
 (0)