[Answered ]-Filtering cities by country in the django admin edit form

2👍

You can customize the form used by the Django admin. In your app, create a form that exhibits the behavior you want. In this case you will probably want to write one that overloads the form’s __init__() method and adjusts the values of your city field dynamically. E.g.:

class LocationAdminForm(ModelForm):
    def __init__(self, *args, **kwargs):
        super(LocationAdminForm, self).__init__(*args, **kwargs)
        if self.instance:
            # we're operating on an existing object, not a new one...
            country = self.instance.country
            cities = # get cities from your master countries list...
            self.fields["new_city"] = ChoiceField(choices=cities)

Once you have a working ModelForm, you can tell the admin to use it in your admin.py:

class LocationAdmin(admin.ModelAdmin):
    form = LocationAdminForm

The admin customization is documented here. An old but still mostly relevant article on dynamic Django forms is here.

To do this for NEW records, you will need to implement most of it on the front-end. You will need Javascript code that retrieves and updates the cities list dynamically as a different country is selected. Your Django app would expose an AJAX view the Javascript can call. It would pass the country and return a simple JSON object of cities, which it would use to update the element in the form.

Alternatively, a pure server-side solution is possible if you change the workflow. This could work like a “wizard” and ask the user to first select the country. Click next to view step 2 of the wizard with a dynamically generated Django form as above. The Django wizard app may be of use.

0👍

I tried the above solution (@jdl2003) but it didn’t work (at least in Django 1.8) and I was getting the same error as @bruno-amaral mentioned. I was able to fix the issue like this:

#forms.py
from django.forms import ModelForm, ModelChoiceField
from cities_light.models import City

class MyUserAdminForm(ModelForm):
    def __init__(self, *args, **kwargs):
        super(MyUserAdminForm, self).__init__(*args, **kwargs)
        if self.instance:
            country = self.instance.country
            cities = country.city_set.all() if country else City.objects.none()
            self.fields['city'] = ModelChoiceField(queryset=cities)

#admin.py
from django.contrib import admin
from django.contrib.auth import get_user_model
from django.contrib.auth.admin import UserAdmin
from myapp.forms import MyUserAdminForm

class MyUserAdmin(UserAdmin):
    form = MyUserAdminForm
    UserAdmin.fieldsets += (
        (None, {'fields': ('dob', 'country', 'city')}),
    )
    UserAdmin.list_display += ('country',)

admin.site.register(get_user_model(), MyUserAdmin)

Only issue I have now, is that cities list is not getting updated dynamically. I need to first save the country field and after that cities are updated.

Leave a comment