Skip to content

feat: support select_related and prefetch_related for inherited models#893

Open
huynguyengl99 wants to merge 4 commits into
django-commons:mainfrom
huynguyengl99:feat/child-select-prefetch-related
Open

feat: support select_related and prefetch_related for inherited models#893
huynguyengl99 wants to merge 4 commits into
django-commons:mainfrom
huynguyengl99:feat/child-select-prefetch-related

Conversation

@huynguyengl99
Copy link
Copy Markdown
Contributor

@huynguyengl99 huynguyengl99 commented May 21, 2026

Hi @bckohan (and other devs), the select_related and prefetch_related support for inherited models has been requested for a long time but hasn't been merged yet despite some pending PRs (#545, #531), so I decided to implement it. It's working well in my project. Hope you guys can find time to review it so it could be merged — I've really wanted this feature for a while.

Summary

Add select_related and prefetch_related support for inherited/child models using the existing ModelName___field syntax.

Parent.objects.select_related('ChildA___fk_field', 'ChildB___other_fk')
Parent.objects.prefetch_related('ChildC___m2m_field')
Parent.objects.prefetch_related(
    Prefetch('Child___m2m', queryset=Related.objects.filter(active=True), to_attr='cached')
)

Child-specific fields are only applied when re-fetching instances of the matching child class, avoiding unnecessary JOINs on other subtypes. The ___ syntax is consistent with filter(), order_by(), defer(), and only().

How it works

select_related() and prefetch_related() are overridden to parse ModelName___field lookups. Child-specific configs are stored on the queryset and applied per-child in _get_real_instances(). Grandchild models inherit configs via issubclass. Handles only()/defer() interaction by auto-including FK fields in the non-deferred set.

vs prior PRs

vs #545#545 takes a more ambitious approach by rewriting the iterator and RelatedPopulator to also support non-poly → poly FK traversal — a great goal. This PR takes a lighter approach (~80 lines, plugging into the existing _get_real_instances() flow) to cover the most common use cases first. The non-poly → poly FK case from #545 could be added on top of this separately.

vs #531#531 adds new methods (select_polymorphic_related) rather than extending existing ones. This PR extends select_related/prefetch_related directly — no new API to learn, consistent with how ___ already works in filter() and order_by(). Also adds handling for only()/defer() interaction, Prefetch objects, to_attr, and clearing with None.

Issues addressed

Closes #198, #436, #498, #641. Partially addresses #410, #359, #545.

Test plan

  • Child-specific select_related / prefetch_related pre-loads relations (0 extra queries)
  • Prefetch object with custom queryset and to_attr
  • Grandchild inherits parent's config
  • Chaining, clearing with None, combined select + prefetch
  • only() + child-specific select_related
  • Base FK select_related preserved after downcast
  • All existing tests pass, PostgreSQL tested

Also tested against a production Django+DRF codebase with N+1 detection — working as expected.

@huynguyengl99 huynguyengl99 requested a review from bckohan as a code owner May 21, 2026 18:43
@huynguyengl99
Copy link
Copy Markdown
Contributor Author

If this looks good, I think it could go out as 4.12 rather than a 5.0 bump — the changes are fully backward compatible. Existing select_related/prefetch_related calls without ___ syntax behave identically to before, and no existing APIs were changed or removed.

@bckohan
Copy link
Copy Markdown
Member

bckohan commented May 31, 2026

@huynguyengl99 thanks for this, I'll look at it asap

@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented Jun 1, 2026

Codecov Report

❌ Patch coverage is 87.61905% with 13 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/polymorphic/query.py 87.61% 8 Missing and 5 partials ⚠️
Files with missing lines Coverage Δ
src/polymorphic/query.py 96.65% <87.61%> (-3.35%) ⬇️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

select_related and prefetch_related for inherited models

3 participants