I'm working on a project where the task is to implement a dynamic drop-down menu where the choices in the menu will change depending on the value that the user selects from a previous drop-down menu.
I have the first dropdown menu populated with the values of the name keys in the JSON below (Network 1, Network 2, etc.). My goal is that the second dropdown will populate with a list of the zone values that correspond with the name value that the user selects in the first dropdown.
So, if the user selects Network 1 from the first dropdown, the second dropdown will populate with network.1.private.zone as its only option. If the user selects Network 2 from the first dropdown, then the second dropdown will populate with network.2.private.zone as its only option, and so on.
Thanks in part to this guide, I've made quite a bit of progress on this. I can see the AJAX request firing in the browser, and the url and networkId values seem to be correct when I console.log them to the browser. But I can't figure out why the dynamic dropdown menu isn't populating. Here is a screenshot of what the page looks like:
Notably, my project does not have any models; rather, all the data I'm working with is stored in the JSON file, referenced above and seen below. What am I missing?
Pertinent code included below:
Here is the JSON file:
[
{
"name": "Network 1",
"onboard": {
"format": "network",
"base": "172.27.22.0",
"mask": "255.255.255.0",
"zones": ["network.1.onboard.zone"]
},
"private": {
"format": "network",
"base": "172.24.16.0",
"mask": "255.255.252.0",
"zones": ["network.1.private.zone"]
},
"public": {
"format": "network",
"base": "128.237.78.0",
"mask": "255.255.255.0",
"zones": ["network.1.public.zone"]
}
},
{
"name": "Network 2",
"onboard": {
"format": "network",
"base": "172.27.23.0",
"mask": "255.255.255.0",
"zones": ["network.2.onboard.zone"]
},
"private": {
"format": "network",
"base": "172.24.234.0",
"mask": "255.255.254.0",
"zones": ["network.2.private.zone"]
},
"public": {
"format": "network",
"base": "172.24.234.0",
"mask": "255.255.254.0",
"zones": ["network.2.public.zone"]
}
},
...
]
utils.py (The JSON file above is referenced here as WIRED_GROUPS)
...
def return_zones_by_network(network_name, network_group=WIRED_GROUPS, private=True):
"""
Helper function that will eventually be used to construct the dynamic dropdown menu.
**** WIRED_GROUPS is the JSON file referenced earlier in this post ****
"""
network_zones = []
for network in network_group:
if network['name'] == network_name:
if private:
for zone in network['private']['zones']:
network_zones.append(zone)
else:
for zone in network['public']['zones']:
network_zones.append(zone)
return network_zones
...
forms.py
class WiredRegistrationForm(Form):
def __init__(self, groups, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['group'].choices = ((0, '---------',),)
self.fields['group'].choices += tuple(
(i+1, groups[i],) for i in range(len(groups))
)
self.fields['hostname_zones'].queryset = ((0, '---------',),)
network = ChoiceField(
# choices=wired_network_choices,
choices=wired_network_choices,
widget=Select(attrs={'class': 'form-control'})
)
hostname = CharField(
label='Hostname',
max_length=63,
error_messages={'incomplete': 'Enter a hostname.'},
validators=[HOSTNAME_VALIDATOR],
widget=TextInput(attrs={
'autocomplete': 'off',
'autocapitalize': 'off'
})
)
# this is the field that needs to be dynamically populated.
hostname_zones = ChoiceField(
choices=[],
widget=Select(attrs={'class': 'form-control'})
)
mac_address = MACAddressField(label="MAC address")
group = ChoiceField(
widget=Select(attrs={'class': 'form-control'}),
)
views.py
def load_hostname_zones(request):
"""
A view to return a list of hostname zones for a given network.
"""
# somehow get the network name that the user selected
network_name = request.GET.get('network')
network_zones = return_zones_by_network(
network_name=network_name
)
return render(request, 'hosts/register/host_zones_dropdown_list_options.html', {'network_zones': network_zones})
def register(request):
"""
The REGISTER view.
"""
if is_requestor_wireless(request):
return redirect('hosts:register_wireless')
return redirect('hosts:register_dorm')
def register_wired(request):
"""
The REGISTER WIRED view.
"""
user = get_remote_user_from_request(request)
if len(get_hosts_for_user(CONN, user)) >= MAX_REGISTRATIONS:
msg = (
'You can\'t register any more devices because you have too many '
'currently registered to your Andrew ID. If you still need '
'additional devices registered, please contact the '
'<a href="https://www.cmu.edu/computing/support/" target="_blank">'
'Help Desk</a> for assistance.'
)
return register_failure(request, msg)
zone = ZONES['wired']['private']
network_list = []
for network in WIRED_GROUPS:
network_list.append(network['name'])
context = {
'network_list': network_list, # the list above
'max_devices': MAX_REGISTRATIONS # 100
'zone': zone
}
groups = get_member_from_request(request)
if request.method != 'POST':
context['form'] = WiredRegistrationForm(groups)
return render(request, 'hosts/register/wired.html', context)
form = WiredRegistrationForm(groups, request.POST)
context['form'] = form
# checks if the form passes all the validation rules
if form.is_valid():
# access the user-input data (pretty sure the data will be a dict)
# process the data... THESE ARE REFERENCED IN THE HTML TEMPLATE AS "form.hostname", "form.mac_address", etc.
data = form.cleaned_data
hostname = data['hostname']
mac_address = data['mac_address']
network_id = int(data['network'])
# this code block references the datacubes library
networks = Network.search(
CONN,
network=WIRED_GROUPS[network_id]['private']['cidr']
)
network = networks[0]
zone = network['private']['zones']
fqdn = f'{hostname}.{zone}'
addr = network.next_available_ip()[0]
ipv4addr = HostIpv4addr(CONN, {
'ipv4addr': addr,
'configure_for_dhcp': True,
'mac': mac_address,
})
choice = form.cleaned_data['group']
extattrs = {'suspend': 0}
if int(choice) > 0:
extattrs['group'] = groups[int(choice)-1]
else:
extattrs['andrewid'] = get_remote_user_from_request(request)
try:
host = Host.create(
CONN, fqdn, ipv4addrs=[ipv4addr], extattrs=extattrs
)
except HTTPError:
msg = (
'A device with the same hostname or MAC address has '
'already been registered.'
)
return register_failure(request, msg)
grid(CONN).restartservices()
return redirect('hosts:register_success', ref=host.objref)
# renders the request to the hosts/register/WIRED page.
return render(request, 'hosts/register/wired.html', context)
urls.py
app_name = 'hosts'
urlpatterns = [
...
url(
r'^register/$',
views.register,
name='register'
),
url(
r'^register/wired',
views.register_wired,
name='register_wired'
),
url(
r'^register/wired/load-hostname-zones',
views.load_hostname_zones,
name='register_wired_load_hostname_zones'
),
...
]
/templates/hosts/register/wired.html
{% extends 'hosts/register/base.html' %}
{% load static %}
{% block css %}
{{ block.super }}
<link href="{% static 'fonts/fontawesome/css/solid.min.css' %}" rel="stylesheet">
<link href="{% static 'fonts/fontawesome/css/fontawesome.min.css' %}" rel="stylesheet">
<link href="{% static 'css/group-hide.css' %}" rel="stylesheet">
{% endblock css %}
{% block breadcrumbs %}
{{ block.super }}
<span>Register Device</span>
<span>  › </span>
<span>Wired Network</span>
{% endblock breadcrumbs %}
{% block content %}
<div class="boxes">
<h1>Register Device - Wired Network</h1>
<p>
Students, faculty, and staff can complete this form to register a computer or device in a campus building. Up to {{ max_devices }}
devices can be registered. Devices are registered with a <em>private IP</em> address, which provides additional
security when accessing Internet resources. Contact the Help Center to request a public IP address for a device.
</p>
<p>
<em>Note</em>: To register a device located in academic or administrative buildings not supported by this application as well as departmental
computers/devices in residence halls, visit
<a href="https://www.cmu.edu/computing/services/endpoint/network-access/wired/how-to/index.html" target="_blank">
How to Connect to the Wired Network
</a>.
</p>
<div>
<form method="post" id="wiredRegistrationForm" action="{% url 'hosts:register_wired' %}" data-zones-url="{% url 'hosts:register_load_hostname_zones' %}" novalidate>{% csrf_token %}
{# <form method="post" id="registerWiredForm" action="{% url 'hosts:register_wired' %}" hostname-zones-url="{% url 'hosts:load_hostname_zones' %}">{% csrf_token %}#}
{# <form method="post" id="registerWiredForm" hostname-zones-url="{% url 'ajax_load_hostname_zones' %}">{% csrf_token %}#}
<fieldset>
{# network field #}
<div>
{{ form.network.label_tag }}
{{ form.network }}
</div>
{# hostname field #}
<div class="">
{{ form.hostname.errors }}
{{ form.hostname.label_tag }}
<div>
{{ form.hostname }}
{# .{{ form.zone }}#}
. {{ form.hostname_zones }}
<p>
A hostname is a nickname used to identify a device connected to the network. It can be comprised of any
string of alphanumeric characters without spaces.
</p>
</div>
</div>
{# mac address field #}
<div>
{{ form.mac_address.errors }}
{{ form.mac_address.label_tag }}
<div>{{ form.mac_address }}</div>
<p>
A MAC address is a unique identifier assigned to a Network Interface Card (NIC) by the manufacturer. Visit
<a href="https://www.cmu.edu/computing/services/endpoint/network-access/wireless/how-to/mac-address.html"
target="_blank">Find My MAC Address</a> for help.
</p>
</div>
<div>
<input type="checkbox" class="groupCheckbox"/>
<label>Register this device for a group or department.</label>
<div class="groupSelector">
<p>Group: {{ form.group }}</p>
</div>
</div>
{# <div>#}
{# {{ form.network.label_tag }}#}
{# {{ form.network }}#}
{# </div>#}
<div>
<div>
<button type="reset" class="btn btn-secondary"><i class="fas fa-undo fa"></i> Reset</button>
<button type="submit" class="btn btn-primary"><i class="fas fa-id-card fa"></i> Submit</button>
</div>
</div>
</fieldset>
</form>
<script src="https://code.jquery.com/jquery-1.10.1.min.js"></script>
<script>
$("#id_network").change(function () {
let url = $("#wiredRegistrationForm").attr('data-zones-url'); // get the URL of the "load_hostname_zones" view
console.log(url);
let networkId = $(this).val(); // get the selected hostname ID from the HTML input
console.log(networkId); // this works (prints out 1, 2, 3, 4, etc., depending on what network you select)
$.ajax({
url: url,
data: {
'network': networkId // add the network id to the GET parameters (is this right???)
},
success: function (data) { // "data" is the return of the "load_hostname_zones" function
$("#id_hostname_zones").html(data); // "data" is defined above...
}
});
});
</script>
</div>
</div>
{% endblock content %}
{% block js %}
{{ block.super }}
<script src="{% static 'js/mac-address.js' %}" type="text/javascript"></script>
{% endblock js %}
/templates/hosts/register/host_zones_dropdown_list_options.html
{# This is only used to compose the tiny bit of HTML that is the hostname zone dropdown menu #}
<option value="">---------</option>
{% for hostname_zone in hostname_zones %}
{#<option value="{{ network_zones.index(network_zone) }}">{{ network_zone }}</option>#}
<option value="{{ hostname_zone.pk }}">{{ hostname_zone.name }}</option>
{% endfor %}
