Skip to main content

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_fields without a dependency mapping continue to use Django's native autocomplete endpoint.
  • TabularInline and StackedInline are 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_city stays 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)

Source distribution for django-admin-dependent-autocomplete 0.2.0
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)

Table of built distributions (wheels) for django-admin-dependent-autocomplete 0.2.0
File Interpreter ABI Platform Actions
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 log

Release 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 log

Security 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.

Select from 2 files

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 log

A 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.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page