[Fixed]-Offering choices that depend on other fields in Django Admin

1๐Ÿ‘

โœ…

I suggest to use a different data structure:

{"USA": ["Alabama", "Alaska"], "Canada": ["Ontario", "Quebec"]}

Then in models.py you can load/store the dict in this way:

class Airport(BaseModel):
    airport_code = models.CharField(max_length=50, verbose_name="Airport Code (ICAO)")
    airport_name = models.CharField(max_length=50, verbose_name="Airport Name")
    city = models.CharField(max_length=50, verbose_name="City")
    country = models.TextField(blank=True, null=True)

def setlist(self, x):
    self.country = json.dumps(x)

@property
def getlist(self):
    if self.country:
        return json.loads(self.country)
    else:
        return {}

In view.py:

@login_required
def show_airport(request):
    airport = Airport.objects.first()

    return render(
        request,
        'your_template.html',
        {'airport': airport}
    )

So in template will be easier to iterate over keys and values to generate related menus:

 {% for key, value in airport.getlist.items %}
    <p>{{key}}: {{value}} </p>
 {% endfor %}

Then you can manage user choices client side with javascript with something like:

                <form>
                    <select id="nation">
                        {% for key, value in airport.getlist.items %}
                        <option value="{{key}}">{{key}}</option>
                        {% endfor %}
                    </select>
                        <select id="states">
                        </select>
               </form>
<script>
      country_list = {{ airport.getlist.items|safe }};
      jQuery.each(country_list[$('#nation').val()], function() {
            $("#states").append('<option value="' + this + '">' + this + '</option>')
        });
     $('#nation').on('change', function(event){
        $('#states').empty()
        jQuery.each(country_list[$('#nation').val()], function() {
            $("#states").append('<option value="' + this + '">' + this + '</option>')
        });
                });
</script>

UPDATE:

Country example using django-autocomplete-light module inside django admin:

pip install django-autocomplete-light

settings.py

INSTALLED_APPS = [
    'dal',
    'dal_select2',
    'django.contrib.admin',
...

urls.py

from django.conf.urls import url
from django.contrib import admin
from countryTest.views import CountryAutocompleteFromList

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^test-country/$', CountryAutocompleteFromList.as_view(), name="country-list-autocomplete")
]

views.py

from dal.autocomplete import Select2ListView


class CountryAutocompleteFromList(Select2ListView):
    def get_list(self):
        continent = self.forwarded.get('continent', None)

        qs = {
            'Usa': ['California', 'Kansas'],
            'Canada': ['Quebec', 'Ontario']}

        if continent:
            return qs[continent]

admin.py

from countryTest.models import Country
from django.contrib import admin
from countryTest.forms import CountryForm


class CountryAdmin(admin.ModelAdmin):
    form = CountryForm
    model = Country


admin.site.register(Country, CountryAdmin)

models.py

from django.db import models


class Country(models.Model):
    continent = models.CharField(max_length=250, null=True)
    state = models.CharField(max_length=250, null=True)

    def __unicode__(self):
        return " ".join((str(self.continent), str(self.state)))

forms.py

from dal import autocomplete

from django import forms


CONTINENT_CHOICES = (('Usa', 'Usa'), ('Canada', 'Canada'))


def get_choices_list():
    return ['Quebec', 'Ontario', 'Kansas', 'California']


class CountryForm(forms.ModelForm):
    continent = forms.ChoiceField(choices=CONTINENT_CHOICES)
    state = autocomplete.Select2ListChoiceField(
        choice_list=get_choices_list,
        widget=autocomplete.ListSelect2(url='country-list-autocomplete', forward=['continent'])
    )
๐Ÿ‘คAndrea

Leave a comment