fix: stop Route admin change page from timing out#424
Merged
Conversation
The Django admin Route change page renders RouteProviderInline and
RouteDestinationInline, each of which renders a <select> of *every*
Integration for every inline row and every extra form. Integration.__str__
touches owner.name and type.name (neither select_related), so building those
dropdowns is an N+1 storm whose cost scales with the integration count -- the
cause of the change page timing out in production.
Adding autocomplete_fields = ("integration",) to both inlines replaces the
full-table dropdowns with AJAX autocomplete (backed by IntegrationAdmin's
existing search_fields), so no Integration options are pre-rendered.
A regression test renders the change page, adds 100 integrations, and asserts
the query count stays flat. Before the fix it went 460 -> 4259 queries; after,
it stays within a small constant.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR addresses production timeouts when rendering the Django admin Route change page by preventing the inlines from pre-rendering a full-table <select> of all Integration rows (which previously triggered N+1 queries via Integration.__str__).
Changes:
- Switched
RouteProviderInlineandRouteDestinationInlineto useautocomplete_fields = ("integration",)to avoid rendering allIntegrationoptions. - Added a regression test that asserts the Route change page query count does not grow materially after adding 100
Integrationrows.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
cdip_admin/integrations/admin.py |
Uses autocomplete_fields on Route-related inlines to avoid full-table dropdown rendering and N+1 queries. |
cdip_admin/integrations/tests/test_admin.py |
Adds a query-count regression test ensuring Route change page rendering stays flat as integrations increase. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
This was referenced Jun 2, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Requesting the Django admin change page for a
Route(e.g./admin/integrations/route/<id>/change/) times out in production.Root cause
The change page renders
RouteProviderInlineandRouteDestinationInline. Each inline renders a<select>of everyIntegration— for every existing row and every extra form.Integration.__str__isf"{self.owner.name} - {self.name} - {self.type.name}", and neitherownernortypeisselect_related, so building each dropdown fires N+1 queries. The total cost scales with the number of integrations, which is large in production → timeout.Quantified with the new regression test: rendering the page went from 460 → 4,259 queries after adding just 100 integrations (~38 queries per integration).
This is distinct from the changelist
COUNT(*)issue fixed in #418 — this is the single-object change view and its inlines.Fix
Add
autocomplete_fields = ("integration",)to both inlines. The FK now renders as an AJAX autocomplete box (backed byIntegrationAdmin's existingsearch_fields) instead of a full-table dropdown, so noIntegrationoptions are pre-rendered and there is no N+1.Testing
integrations/tests/test_admin.pyrenders the change page, adds 100 integrations, and asserts the query count stays flat (delta ≤ 10). Fails before the fix, passes after.manage.py checkpasses (confirms theautocomplete_fieldsconfig is valid and won't break admin startup).Note for reviewers
Staff users need view permission on
Integrationfor the autocomplete results to populate (standard Django autocomplete behavior). Superusers and typical admin roles already have it.🤖 Generated with Claude Code