Filter city field (for each province) based on selected province field in django admin panel

class Province(models.Model):
    name = models.CharField( max_length=70, unique=True, null=True, blank=True,)
    def __str__(self):
        return self.name
class City(models.Model):
    province = models.ForeignKey("election.Province", on_delete=models.CASCADE)
    name = models.CharField(max_length=70, unique=True, null=True, blank=True,)
class Election(models.Model):
    province = models.ForeignKey("election.Province", on_delete=models.SET_NULL, null=True, blank=True,) 
    city = models.ForeignKey("election.City", on_delete=models.SET_NULL, null=True, blank=True,)

I have this models.

in admin panel, when selecting a province I want to just see the cites that belonging to that province.
I`m begginer ;)

Dynamically, without extra JS workload*, you can't :/

However if Election model is already saved and province on it set, you can limit queryset for cities with:

class ElectionAdmin(...):
[...]
    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        if db_field.name == "city":
            election_id = request.resolver_match.kwargs.get("object_id")
            election = Election.objects.filter(id=election_id).first()
            if election and election.province:
                kwargs["queryset"] = City.objects.filter(province=election.province)
        return super().formfield_for_foreignkey(db_field, request, **kwargs)

--

* JS algorithm would look something like:

  1. add event listener (on 'input') to Province input
  2. query CityAdmin (look into ModelAdmin.search_fields prop) for cities with queried province set
  3. update City input (select options in this case)

You might also take a closer look at ModelAdmin.autocomplete_fields

An idea could be to work with django-smart-selects [readthedocs.io]. You install this by following the installation section of the documentation [readthedocs.io], and redefine the city field as:

from smart_selects.db_fields import ChainedForeignKey


class Election(models.Model):
    province = models.ForeignKey(
        'election.Province', on_delete=models.SET_NULL, null=True, blank=True
    )
    city = ChainedForeignKey(
        'election.City',
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        chained_field='province',
        chained_model_field='province',
        show_all=False,
        auto_choose=True,
        sort=True,
    )

This will make AJAX requests to dynamically filter the items based on the other field(s).

Back to Top