Django Динамическое выпадающее меню не заполняется

Я работаю над проектом, в котором стоит задача реализовать динамическое выпадающее меню, в котором варианты выбора в меню будут меняться в зависимости от значения, которое пользователь выбирает из предыдущего выпадающего меню.

У меня есть первое выпадающее меню, заполненное значениями ключей name в JSON ниже (Network 1, Network 2 и т.д.). Моя цель состоит в том, чтобы второе выпадающее меню заполнялось списком значений zone, которые соответствуют значению name, выбранному пользователем в первом выпадающем меню.

Итак, если пользователь выбирает Network 1 из первого выпадающего списка, то второй выпадающий список будет содержать network.1.private.zone в качестве единственного варианта. Если пользователь выберет Network 2 из первого выпадающего списка, то второй выпадающий список будет содержать network.2.private.zone в качестве единственного варианта, и так далее.

Отчасти благодаря этому руководству, я довольно сильно продвинулся в этом деле. Я вижу, как AJAX-запрос выполняется в браузере, и значения url и networkId кажутся правильными, когда я console.log передаю их в браузер. Но я не могу понять, почему динамическое выпадающее меню не заполняется. Вот скриншот того, как выглядит страница:

Django project page with dropdowns and browser console showing.

Примечательно, что в моем проекте нет никаких моделей; скорее, все данные, с которыми я работаю, хранятся в файле JSON, ссылка на который приведена выше и показана ниже. Что я упускаю?

Ниже приводится код:

Вот файл JSON:

[
  {
    "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 (Файл JSON, приведенный выше, упоминается здесь как 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

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

/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 %}

Итак, я разобрался с этим. Проблема заключалась в несоответствии между моей вспомогательной функцией в файле utils.py, которую можно увидеть здесь:

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  # returns a list, not a dict

и крошечный шаблон в /templates/hosts/register/host_zones_dropdown_list_options.html, который можно увидеть здесь:

<option value="">---------</option>
{% for hostname_zone in hostname_zones %}
  <option value="{{ hostname_zone.pk }}">{{ hostname_zone.name }}</option>  # this line here
{% endfor %}

В шаблоне я ссылался на свойства каждого hostname_zone (pk и name), когда в действительности каждый hostname_zone является строкой из списка. Наверное, я немного удивлен, что я не получил что-то вроде ошибки атрибута при ссылке на несуществующие свойства, но теперь выпадающее меню динамически заполняется, как и должно.

Ниже приведен исправленный шаблон. Надеюсь, это поможет кому-нибудь еще на этом пути:

<option value="">---------</option>
{% for hostname_zone in hostname_zones %}
  <option value="">{{ hostname_zone }}</option>
{% endfor %}
Вернуться на верх