django-admin-dependent-autocomplete
django-admin-dependent-autocomplete adds a single capability to Django
Admin's built-in autocomplete: a ForeignKey autocomplete can be constrained
by another ForeignKey in the same admin form.
It deliberately reuses Django Admin's autocomplete_fields, Select2 bundle,
AutocompleteJsonView, permissions, search_fields, pagination, and JSON
format. It does not introduce a second autocomplete implementation or any
runtime dependency besides Django.
Install
pip install django-admin-dependent-autocomplete
Add django_admin_dependent_autocomplete to INSTALLED_APPS so Django can
discover its static JavaScript file.
Simple shorthand dependency
from django.contrib import admin
from django_admin_dependent_autocomplete.admin import DependentAutocompleteAdminMixin
from .models import Address, City, Country, State
@admin.register(Country)
class CountryAdmin(admin.ModelAdmin):
search_fields = ["name"]
@admin.register(State)
class StateAdmin(admin.ModelAdmin):
search_fields = ["name"]
@admin.register(City)
class CityAdmin(admin.ModelAdmin):
search_fields = ["name"]
@admin.register(Address)
class AddressAdmin(DependentAutocompleteAdminMixin, admin.ModelAdmin):
autocomplete_fields = ["state", "city"]
autocomplete_dependencies = {
"state": "country",
"city": "state",
}
For the configuration above, Address.state depends on Address.country, and
Address.city depends on Address.state. The related models use matching
ForeignKey relationships:
State.objects.filter(country_id=country_id)
City.objects.filter(state_id=state_id)
search_fields on the target model's ModelAdmin is required by Django
Admin for every field listed in autocomplete_fields.
Target fields with a different lookup name
The short form above uses the same field name on the source form and on the target model. When those names differ, configure the target model field explicitly:
autocomplete_dependencies = {
"city": {
"depends_on": "state",
"lookup": "state",
},
}
Here state is the field on the source admin form and lookup names the
ForeignKey on the target autocomplete model when validation needs to be
explicit. Both names are checked server-side; the browser only ever supplies
the selected parent primary key.
The package installs a small per-admin URL that delegates to Django's native
autocomplete view. It applies the configured parent filter before invoking the
related model admin's get_search_results(). Permissions, search_fields,
custom get_search_results(), limit choices, pagination, and the native JSON
response remain in use. The browser only sends a parent value; it never sends
a lookup name, and the relationship is validated server-side from the mapping.
Changing a parent clears its dependent child. Existing values on a change form are left intact until the parent changes. When the parent is empty, its dependent autocomplete returns no results.
Inline dependencies
Version 0.2 adds formal support for dependent autocompletes inside direct
inlines. Both the hosting ModelAdmin and the inline class must use
DependentAutocompleteAdminMixin.
Same inline row
When source is omitted, the parent field is resolved in the same inline row
or main form field prefix:
autocomplete_dependencies = {
"city": "state",
}
Parent form source
Use source="parent" when the parent ForeignKey lives on the hosting
ModelAdmin change form rather than in the inline row:
from django.contrib import admin
from django_admin_dependent_autocomplete.admin import DependentAutocompleteAdminMixin
from .models import City, Country, Person, PersonLocation, State
@admin.register(Country)
class CountryAdmin(admin.ModelAdmin):
search_fields = ["name"]
@admin.register(State)
class StateAdmin(admin.ModelAdmin):
search_fields = ["name"]
@admin.register(City)
class CityAdmin(admin.ModelAdmin):
search_fields = ["name"]
class PersonLocationInline(DependentAutocompleteAdminMixin, admin.TabularInline):
model = PersonLocation
autocomplete_fields = ["state", "city", "backup_city"]
autocomplete_dependencies = {
"state": {
"depends_on": "country",
"lookup": "country",
"source": "parent",
},
"city": "state",
}
@admin.register(Person)
class PersonAdmin(DependentAutocompleteAdminMixin, admin.ModelAdmin):
autocomplete_fields = ["country"]
inlines = [PersonLocationInline]
| Configuration | Parent field location | Child field location |
|---|---|---|
"city": "state" |
Same inline row | Inline row |
source="parent" |
Hosting ModelAdmin form | Inline row |
Requirements and limits:
source="parent"is only valid on direct inlines, not on the hosting ModelAdmin itself.- Nested inline parent dependencies are not supported.
- The browser still sends only
dependent_parent=<pk>; lookup and config remain server-owned. - Normal
autocomplete_fieldswithout a dependency mapping continue to use Django's native autocomplete endpoint. TabularInlineandStackedInlineare supported through the same mixin.
The inline model does not need its own ModelAdmin registration. The hosting
ModelAdmin registers one dependent-autocomplete URL per inline class that defines
autocomplete_dependencies.
Chained dependencies
The bundled test app demonstrates a Country → State → City chain on
AddressAdmin:
class AddressAdmin(DependentAutocompleteAdminMixin, admin.ModelAdmin):
autocomplete_fields = ["state", "city", "backup_city"]
autocomplete_dependencies = {
"state": "country",
"city": "state",
}
Here Address.country, state, and city are the form fields, while
State.country and City.state are the target model ForeignKey fields.
Changing a country clears its state and city; changing a state clears its city.
Saved values remain visible on change forms until the user changes an ancestor
field. backup_city stays a normal autocomplete with no dependency mapping.
Mixing with a project ModelAdmin
Put DependentAutocompleteAdminMixin before your ModelAdmin subclass:
class AddressAdmin(DependentAutocompleteAdminMixin, ProjectModelAdmin):
autocomplete_fields = ["state", "city"]
autocomplete_dependencies = {
"state": "country",
"city": "state",
}
The mixin uses cooperative super() for formfield_for_foreignkey(),
get_urls(), and check(). A custom formfield_for_foreignkey() further
down the MRO can still explicitly provide a widget; in that case it takes
precedence. Existing get_form() implementations are unaffected.
Current limits
Version 0.2 supports one ForeignKey parent and one ForeignKey autocomplete
child per mapping entry on main forms and direct inlines. It does not support
many-to-many fields, non-admin forms, generic foreign keys, arbitrary callbacks,
multiple parents for one child, nested inline parent dependencies, or sibling
inline dependencies. Multiple entries can form chained dependencies within the
same form or inline row.
Compared with django-autocomplete-light
django-autocomplete-light is a full-featured autocomplete framework. This package intentionally does much less: it extends Django Admin's built-in autocomplete with dependent field filtering.
Compatibility
The package requires Python 3.8+ and supports Django 3.2, 4.2, 5.0, 5.1, and
5.2. CI covers those Django release lines, including Python 3.8 with Django
3.2. Django is the only runtime dependency. The package uses Django Admin's
native AutocompleteSelect widgets and remains compatible with admin themes
that preserve those widgets.
Run the included test app
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
python testapp/manage.py migrate
python testapp/manage.py createsuperuser
python testapp/manage.py seed_data
python testapp/manage.py runserver
Open /admin/testapp/address/add/ for the same-form Country → State → City
demo, or /admin/testapp/person/add/ for the inline parent-form demo with the
same United States / Canada geography.
Manual smoke test for inline dependencies
On /admin/testapp/person/add/ or change:
- Add multiple inline rows and verify each row filters state by the selected country.
- Change the country and confirm dependent state and city values clear;
backup_citystays unchanged. - Add a dynamic inline row and verify dependent autocompletes still filter correctly.
- Reopen a saved person and confirm saved state and city remain visible until a parent changes.
Development checks
pytest
ruff check .
python -m build
twine check dist/*
To run the oldest supported environment locally, install the development
dependencies with Python 3.8 and Django 3.2, then run pytest again.
Roadmap
Possible future additions include multiple parent dependencies, more relation types, and nested inline parent dependencies, without changing the simple mapping used in v0.2.
Metadata
Release files for django-admin-dependent-autocomplete 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| django_admin_dependent_autocomplete-0.2.0.tar.gz | 17.5 kB | With provenance | Details for django_admin_dependent_autocomplete-0.2.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| django_admin_dependent_autocomplete-0.2.0-py3-none-any.whl | Python 3 | none | any | Details for django_admin_dependent_autocomplete-0.2.0-py3-none-any.whl |
Total release size: 29.6 kB
Release files / django_admin_dependent_autocomplete-0.2.0.tar.gz
| Download URL | django_admin_dependent_autocomplete-0.2.0.tar.gz |
|---|---|
| Size | 17.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c54b01079f5062ea0ef936d2fe4c261cb420f8492f318c7abe1f5b5d474d73df
|
|
BLAKE2b-256 checksum How to use checksums |
10a2f309a9c3f0168c9835e9da626065c1d7e72958514ba23800054ae6aaad15
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 28, 2026.
Transparency logRelease files / django_admin_dependent_autocomplete-0.2.0-py3-none-any.whl
| Download URL | django_admin_dependent_autocomplete-0.2.0-py3-none-any.whl |
|---|---|
| Size | 12.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
72359b23634fdfdcc52c3a86a84baee014f55ca42cde79f54d429a7a6db42256
|
|
BLAKE2b-256 checksum How to use checksums |
69118e1160a138b50d825c73e4c62dfb63cf262ebb5878ea7c058f21fc3ecebb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 28, 2026.
Transparency logSecurity for django-admin-dependent-autocomplete 0.2.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
Both files in this release have been uploaded with provenance metadata.
Attestation for django_admin_dependent_autocomplete-0.2.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 28, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.