CalMac
CalMac

Reputation: 459

How to populate a select list in a Django Template dynamically using Javascript?

I've recently been learning Django and HTML but I'm completely new to JavaScript. I'm having a go at creating a database display page with a filter menu at the side. For this page I have the following code:

Model.py:

class Part(models.Model):
    PartID = models.AutoField(primary_key=True, unique=True)
    SiteID = models.ForeignKey('Site', on_delete=models.CASCADE, null=True)
    Comment = models.CharField(max_length=255, blank=True)
    Subtype = models.ForeignKey('Subtype', on_delete=models.CASCADE, null=True)
    Location = models.CharField(max_length=255, blank=True)
    ConnectedTo= models.ManyToManyField('self', null=True)
    BatchNo = models.CharField(max_length=32, blank=False, null=True)
    SerialNo = models.CharField(max_length=32,blank=True)
    Manufacturer = models.CharField(max_length=32, blank=False, null=True)
    Length = models.FloatField(blank=True, null=True)
    InspectionPeriod = models.IntegerField(blank=True, null=True)
    LastInspected = models.DateField(blank=True, null=True)
    InspectionDue = models.CharField(max_length=255, blank=True)

View.py:

@login_required(login_url='/accounts/login/')
def sites(request, site):

    siteselected = site
    warnings = 0
    expired = 0
    good = 0
    PartsAtSite = Part.objects.filter(SiteID = siteselected)
    TypesList = Type.objects.values_list('TypeName', flat=True).distinct()
    InspectionList = Part.objects.values_list('InspectionPeriod', flat=True).distinct()
    LengthList = Part.objects.values_list('Length', flat=True).distinct()
    LocationList = Part.objects.values_list('Location', flat=True).distinct()
    ManufacturerList = Part.objects.values_list('Manufacturer', flat=True).distinct()

    for part in PartsAtSite:
        if part.LastInspected == None:
            part.InspectionDue = "Yes"
            expired = expired + 1

        else:
            Deadline = part.LastInspected + timedelta(days=part.InspectionPeriod)

        if datetime.now().date() > Deadline:
            part.InspectionDue = "Yes"
            expired = expired + 1

        elif datetime.now().date() > (Deadline - timedelta(days=30)):
            part.InspectionDue = "<30 Days"
            warnings = warnings + 1
        else:
            part.InspectionDue = "No"
            good = good + 1
    part.save()

    context = {
        'TypesList': TypesList,
        'InspectionList': InspectionList,
        'LengthList': LengthList,
        'LocationList': LocationList,
        'ManufacturerList': ManufacturerList,
        'PartsAtSite': PartsAtSite,
        'expired': expired,
        'warnings': warnings,
        'good': good,
        'SiteName': Site.objects.get(SiteID = siteselected).SiteName,
        'SiteNo': Site.objects.get(SiteID = siteselected).SiteID,
    }

template = loader.get_template('myproject/sites.html')
return HttpResponse(template.render(context, request))

And the HTML for my filter div:

<div id="filterdiv" class="dark">
    <center><h3>Filters</h3></center>
    <br>

    <center>Type</center>
    <select name="Types">
        <option>All</option>
        {% for types in TypesList %}
            <option>{{types}}</option>
        {%endfor%}
    </select>
    <br>
    <br>

    <center>Inspection Period</center>
    <select name="Inspection Period">
        <option>All</option>
        {% for inspections in InspectionList %}
            <option>{{inspections}}</option>
        {%endfor%}
    </select>
    <br>
    <br>

    <center>Length</center>
    <select name="Length">
        <option>All</option>
        {% for lengths in LengthList %}
            <option>{{lengths}}</option>
        {%endfor%}
    </select>
    <br>
    <br>

    <center>Location</center>
    <select name="Location">
        <option>All</option>
        {% for locations in LocationList %}
            <option>{{locations}}</option>
        {%endfor%}
    </select>
    <br>
    <br>

    <center>Manufacturer</center>
    <select name="Manufacturer">
        <option>All</option>
        {% for manufacturers in ManufacturerList %}
            <option>{{manufacturers}}</option>
        {%endfor%}
    </select>
    <br>
    <br>
    <button>Apply Filter (TODO)</button>
    <button>Reset Filters (TODO)</button>
</div>

I am able to fill the lists just fine, but I want to be able to change it so that when I open the Type drop down box after selecting a Manufacturer, then I only get offered a list of Types from that Manufacturer.

Thanks.

Upvotes: 0

Views: 2907

Answers (2)

May.D
May.D

Reputation: 1910

Using Javascript, you could add event listeners on your select elements that will trigger a request to your python backend. However you will have to edit the view so that it returns only data (usually in JSON format) in order to know which options you hide or not. Below code suppose that the view returns a simple list, you'll probably have to edit it.

document.getElementById("yourSelect").addEventListener("change", function () {
    // add a handler to the change event
    let req = new XMLHttpRequest(),
        other_select = document.getElementById("anotherSelect");
    req.open("POST", "yourUrl", true);
    // you will need to use json.loads in python code
    req.send(JSON.stringify({value: this.value}));
    req.onreadystatechange = function (res) {
        if (this.readyState === XMLHttpRequest.DONE && this.status === 200) {
            // the request is done and server returned 200 success code
            for (let i in other_select.children){
                let opt = other_select.children[i];
                if(res.indexOf(opt.value) > -1 ){
                    // shows the option if previously hidden
                    opt.style.display = "block";
                }else{
                    // hides the option
                    opt.style.display = "none";
                }
            }
        }
    }
});

Upvotes: 0

Related Questions