Django : Dynamically set dropdown options based on other dropdown selection in Django Model Forms

Viewed 121

This is a common scenario in frontend where we want a dropdown options set with respect to another selection in another dropdown, but not getting a solution in django admin forms Scenario : django model:

class CompanyEmployee():
    """
    An Emailed Report
    """
    company = models.ForeignKey(Company,
                              on_delete=models.CASCADE,
                               )
    employee = models.ManyToManyField(Employee,
                                   blank=True,
                                   verbose_name='Employes',)

    class Meta:
        unique_together = (
            ('company', 'name'),
        )

so in CompanyEmailAdminForm company is in list_filter and Employee as filter_horizontal, that means company is a dropdown and employee as filter with multiple choice.

The queryset for employee widget

     if instance.pk:
            self.fields['employee'].queryset =Employee.objects.filter(company=instance.company)
        else:
            self.fields['employee'].queryset =Employee.objects.all()

Company and Employee have a relation. So from company I can get the related Employee records.

The issue is in add form where I don't have a saved instance.

My requirement is when I select a company say 'ABC' I should get only records related to 'ABC' in the Employee filter.

If onChange i can get the value back in the form I can re-evaluate the employee queryset. With django.JQuery the values in the employee section is not remaining permanently.

1 Answers

Started coding in Django few weeks ago.

I didn't understand well some of your data, but I'll share with you how I made dependent dropdowns with Django in a simple way. I used javascript too.

You need to add the following to your template code:

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>

Then, you must add to your template two select forms, one for each field. One will be for the parent field, the other one for the child. Structure may be like this:

  • Parent select
<select id="field1" name="field1" class="form-control">
    <option hidden selected value="" id="nullfield1">Select field 1</option>
    <option value="A">A</option>
    <option value="B">B</option>
    <option value="C">C</option>
</select>
  • Child select
<select id="field2" name="field2" class="form-control">
    <option hidden selected value="" id="nullfield2">Select field 2</option>
    <option value="1" parent="A">1</option>
    <option value="2" parent="A">2</option>
    <option value="3" parent="B">3</option>
    <option value="4" parent="B">4</option>
    <option value="5" parent="C">5</option>
    <option value="6" parent="C">6</option>
</select>

Then, you add to your template the following Javascript code:

<script>
    var document = window.document;
    $(document).ready(function(){

        var $field1var=$("#field1");
        var $field2var=$("#field2");

        var $field2options=$field2var.find('option');

        $field2var.html($field2options.filter('[value=""]'));

        $field1var.on('change',function(){
            $field2var.html($field2options.filter('[parent="'+this.value+'"],[value=""]'));
            $('#field2 option[value=""]').prop('selected', true);

        });
    });
</script>

After this, the second field options will change as you modify first field.

Watch the structure and copy it for your work.

I'll leave you a simple and completed template you can render to try it.

<html>
    <body>
        <div class="container">
            <div class="row">
                <select id="field1" name="field1" class="form-control">
                    <option hidden selected value="" id="nullfield1">Select field 1</option>
                    <option value="A">A</option>
                    <option value="B">B</option>
                    <option value="C">C</option>
                </select>
                <select id="field2" name="field2" class="form-control">
                    <option hidden selected value="" id="nullfield2">Select field 2</option>
                    <option value="1" parent="A">1</option>
                    <option value="2" parent="A">2</option>
                    <option value="3" parent="B">3</option>
                    <option value="4" parent="B">4</option>
                    <option value="5" parent="C">5</option>
                    <option value="6" parent="C">6</option>
                </select>
            </div>
        </div>
    </body>
</html>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script>
    var document = window.document;
    $(document).ready(function(){
        var $field1var=$("#field1");
        var $field2var=$("#field2");
        var $field2options=$field2var.find('option');
        $field2var.html($field2options.filter('[value=""]'));
        $field1var.on('change',function(){
            $field2var.html($field2options.filter('[parent="'+this.value+'"],[value=""]'));
            $('#field2 option[value=""]').prop('selected', true);
        });
    });
</script>

Hope this is what you needed!!!

Related