django & ajax 依赖的 html 选择列表(级联下拉列表)

Posted

技术标签:

【中文标题】django & ajax 依赖的 html 选择列表(级联下拉列表)【英文标题】:django & ajax dependant html select lists (cascade drop down lists) 【发布时间】:2015-02-25 10:52:19 【问题描述】:

我是 Django 新手(我使用的是 Django 1.4 和 python 2.7),无法理解如何完成以下问题。

我在 SO 和 Google 上做了很多搜索,到目前为止,我对如何解决这个问题感到非常困惑。

我在表单上有两个 html 选择列表 - 行业和部门。当用户从 Industry html 选择列表中选择 Engineering 时,Sector html 选择列表应该动态填充只有 Engineering 选项。与行业 html 选择列表中的其他选择相同。 Sector html 选择列表应该在不刷新页面的情况下动态填充 - 所以我假设必须使用 JQuery / AJAX。

我不太确定我是否正确设置了下面的 UserProfile 模型。 Industry & Sector html 选择列表出现在表单和工作中,但彼此不依赖 - 它们是单独的模型。PositiveIntegerField 字段。也许 Industry & Sector 值应该是下面 UserProfile 模型上的外键。 我需要一些建议。

这是我的用户 models.py 文件:

class UserProfile(models.Model):

    SELECT_INDUSTRY = 0
    ACCOUNTING = 1
    ADMINISTRATION_OFFICE_SUPPORT = 2
    BANKING_FINANCIAL_SERVICES = 3
    CALL_CENTRE_CUSTOMER_SERVICE = 4
    COMMUNITY_SERVICES_DEVELOPMENT = 5
    CONSTRUCTION = 6
    CONSULTING_STRATEGY = 7
    DESIGN_ARCHITECTURE = 8
    EDUCATION_TRAINING = 9
    ENGINEERING = 10
    EXECUTIVE_GENERAL_MANAGEMENT = 11
    FARMING_ANIMALS_CONSERVATION = 12
    GOVERNMENT_DEFENCE = 13
    GRADUATE_ENTRY_LEVEL = 14
    HEALTHCARE_MEDICAL = 15
    HOSPITALITY_TRAVEL_TOURISM = 16
    HUMAN_RESOURCES_RECRUITMENT = 17
    INSURANCE_SUPERANNUATION = 18
    INFORMATION_TECHNOLOGY_TELECOMMUNICATIONS = 19
    LEGAL = 20
    MANUFACTURING = 21
    MARKETING_COMMUNICATIONS = 22
    MEDIA_ADVERTISING_ARTS_ENTERTAINMENT = 23
    MINING_RESOURCES_ENERGY = 24
    REAL_ESTATE_PROPERTY = 25
    RETAIL_CONSUMER_PRODUCTS = 26
    SALES = 27
    SCIENCE_TECHNOLOGY = 28
    SELF_EMPLOYMENT = 29
    SPORT_RECREATION = 30
    TRADES_SERVICES = 31
    TRANSPORT_LOGISTICS = 32


    USER_PROFILE_CURRENT_INDUSTRY_TYPES = (
        (SELECT_INDUSTRY, _('Select Current Industry')),
        (ACCOUNTING, _('Accounting')),
        (ADMINISTRATION_OFFICE_SUPPORT, _('Administration & Office Support')),
        (BANKING_FINANCIAL_SERVICES, _('Banking & Financial Services')),
        (CALL_CENTRE_CUSTOMER_SERVICE, _('Call Centre & Customer Service')),
        (COMMUNITY_SERVICES_DEVELOPMENT, _('Community Services & Development')),
        (CONSTRUCTION, _('Construction')),
        (CONSULTING_STRATEGY, _('Consulting & Strategy')),
        (DESIGN_ARCHITECTURE, _('Design & Architecture')),
        (EDUCATION_TRAINING, _('Education & Training')),
        (ENGINEERING, _('Engineering')),
        (EXECUTIVE_GENERAL_MANAGEMENT, _('Executive & General Management')),
        (FARMING_ANIMALS_CONSERVATION, _('Farming, Animals & Conservation')),
        (GOVERNMENT_DEFENCE, _('Government & Defence')),
        (GRADUATE_ENTRY_LEVEL, _('Graduate / Entry Level')),
        (HEALTHCARE_MEDICAL, _('Healthcare & Medical')),
        (HOSPITALITY_TRAVEL_TOURISM, _('Hospitality, Travel & Tourism')),
        (HUMAN_RESOURCES_RECRUITMENT, _('Human Resources & Recruitment')),
        (INSURANCE_SUPERANNUATION, _('Insurance & Superannuation')),
        (INFORMATION_TECHNOLOGY_TELECOMMUNICATIONS, _('Information Technology & Telecommunications')),
        (LEGAL, _('Legal')),
        (MANUFACTURING, _('Manufacturing')),
        (MARKETING_COMMUNICATIONS, _('Marketing & Communications')),
        (MEDIA_ADVERTISING_ARTS_ENTERTAINMENT, _('Media, Advertising, Arts & Entertainment')),
        (MINING_RESOURCES_ENERGY, _('Mining, Resources & Energy')),
        (REAL_ESTATE_PROPERTY, _('Real Estate & Property')),
        (RETAIL_CONSUMER_PRODUCTS, _('Retail & Consumer Products')),
        (SALES, _('Sales')),
        (SCIENCE_TECHNOLOGY, _('Science & Technology')),
        (SELF_EMPLOYMENT, _('Self Employment')),
        (SPORT_RECREATION, _('Sport & Recreation')),
        (TRADES_SERVICES, _('Trades & Services')),
        (TRANSPORT_LOGISTICS, _('Transport & Logistics'))
    )


    SELECT_SECTOR_TYPE = 0
    _ALL_ACCOUNTING_JOBS = 1
    ....(culled for brevity)
    _ALL_ENGINEERING_JOBS = 124
    AEROSPACE_ENGINEERING = 125
    AUTOMOTIVE_ENGINEERING = 126
    BUILDING_SERVICES_ENGINEERING = 127
    CHEMICAL_ENGINEERING = 128
    CIVIL_STRUCTURAL_ENGINEERING = 129
    ELECTRICAL_ELECTRONIC_ENGINEERING = 130
    ENGINEERING_DRAFTING = 131
    ENVIRONMENTAL_ENGINEERING = 132
    FIELD_ENGINEERING = 133
    INDUSTRIAL_ENGINEERING = 134
    MAINTENANCE = 135
    MANAGEMENT = 136
    MATERIALS_HANDLING_ENGINEERING = 137
    MECHANICAL_ENGINEERING = 138
    PROCESS_ENGINEERING = 139
    PROJECT_ENGINEERING = 140
    PROJECT_MANAGEMENT = 141
    SUPERVISORS = 142
    SYSTEMS_ENGINEERING = 143
    WATER_WASTE_ENGINEERING = 144
    OTHER_ENGINEERING_JOBS = 145
    _ALL_EXECUTIVE_GENERAL_MANAGEMENT_JOBS = 146
    ....(culled for brevity)
    OTHER_TRANSPORT_LOGISTICS_JOBS = 462


    USER_PROFILE_CURRENT_SECTOR_TYPES = (
        (SELECT_SECTOR_TYPE, _('Select Current Sector')),
        .......(culled for brevity)
        (OTHER_TRANSPORT_LOGISTICS_JOBS, _('Other Transport & Logistics Jobs'))
    )

    user = models.OneToOneField(User)
    ....(culled for brevity)
    current_industry_type = models.PositiveIntegerField(choices=USER_PROFILE_CURRENT_INDUSTRY_TYPES, default=SELECT_INDUSTRY, validators=[MinValueValidator(1)])
    current_sector_type = models.PositiveIntegerField(choices=USER_PROFILE_CURRENT_SECTOR_TYPES, default=SELECT_SECTOR_TYPE, validators=[MinValueValidator(1)])
    ....(culled for brevity)
    .

我见过django-smart-selects,但我不确定这是一个动态解决方案,我不确定是否必须为 Industry & Sector 添加单独的模型,然后将 Industry & Sector 的外键添加到上面的 UserProfile 模型。

我希望我可以通过 AJAX 或 JQuery 轻松获得相互依赖的 Industry & Sector html 选择列表。

我们将不胜感激任何建议和帮助。

【问题讨论】:

【参考方案1】:

当我遇到这个问题时,我使用 AJAX 来创建 2 个选择依赖项。首先,我使用 Django 表单,例如:

# I avoid the importation of the choices to make answer shorter
class YourForm(forms.Form):    
    industry = forms.ChoiceField(choices=USER_PROFILE_CURRENT_INDUSTRY_TYPES)
    sector = forms.ChoiceField(choices=USER_PROFILE_CURRENT_SECTOR_TYPES)    
    # ... other fields

我将避免视图的基础知识(如何管理 GET/POST 方法)和基本的 HTML django 表单,我将直接进入 AJAX 功能

假设选择器 ID 为:#id_sector#id_industry

function get_industry()
            jQuery.ajax(
              async: false,
              type: "POST",
              url: "/a/get/industry/",
              data: "sector_id=" + $('#id_sector').val(),
              success: function(response) 
                    result = JSON.parse(response);
                    if (result) 
                        // I usually receive a list of items here
                        // I use this list to replace the dependant select                                                

                        $('#id_industry').empty()  // Use to empty the select

                        // Now we append the industry options we've received
                        for(var i=0;i < result.item_list.length;i++)
                            $('#id_industry').append($('<option>',  
                                value: result.item_list[i]['id'],
                                text: result.item_list[i]['name'] 
                        ));      
                        

                     else 
                        console.log('error');
                    
                
            );
        

function get_sector()
            jQuery.ajax(
              async: false,
              type: "POST",
              url: "/a/get/sector/",
              data: "industry_id=" + $('#id_industry').val(),
              success: function(response) 
                    result = JSON.parse(response);
                    if (result) 
                        $('#id_sector').empty()  // Use to empty the select

                        // Now we append the sector options we've received
                        for(var i=0;i < result.item_list.length;i++)
                            $('#id_sector').append($('<option>',  
                                value: result.item_list[i]['id'],
                                text: result.item_list[i]['name'] 
                        ));                                                   
                        

                     else 
                        console.log('error');
                    
                
            );
        

现在我将展示 AJAX 视图。您可以在名为 ajax.py 的文件中设置 AJAX 视图

from yourapp.models import USER_PROFILE_CURRENT_SECTOR_TYPES

INDUSTRY_DICT = 
    4: range(14,36),
    5: range(36,58),
    6: range(58,80),
    7: range(80,102),
    8: range(102,124),
    10: range(124,146)  # This is the only true equivalence that you passed to me


@csrf_exempt
def get_sectors(request):
    response = []
    industry_id = int(request.POST['industry_id'])

    # With the sector_id you know wich sector the user has selected
    # You should generate the list based in your needs
    data = []
    if industry_id:
        sectors = INDUSTRY_DICT[industry_id]  # This return a list of ID's of sectors
        # Then make loop over all sectors
        for sector_id in sectors:  
            # To get the sector name you should use another dict
            # I think you already have it in USER_PROFILE_CURRENT_SECTOR_TYPES
            # Remember to import it (check above)
            sector_name =  USER_PROFILE_CURRENT_SECTOR_TYPES[sector_id]
            # We append the id and the name to replace options in the HTML
            data.append('id':sector_id, 'name':sector_name)  

        response =  'item_list':data   # We send back the list
        return HttpResponse(simplejson.dumps(response))

    # If we get any error, or cannot get the sector, we send an empty response
    response = 
    return HttpResponse(simplejson.dumps(response))

我将避免添加第二个 AJAX 函数 'get_sectors' 因为我假设你理解逻辑,它应该与这个函数相同,但你会收到 industry_id 而不是sector_id,我认为你可以面对第二个功能。

设置 url 之前的最后一步是定义管理选择更改并调用 AJAX 函数的函数:

    $("#id_sector").change(function()           
            get_industry();  // AJAX function call when sector is selected           
    );

    $("#id_industry").change(function()           
            get_sector();  // AJAX function call when industry is selected             
    );

您需要在 urls.py 中添加两个网址:

# ... YOUR OTHER URLS
url(r'^a/get/industry/?$', 'yourproject.ajax.get_industries', name='get_industries'),
url(r'^a/get/sector/?$', 'yourproject.ajax.get_sectors', name='get_sectors'),

提示:

当您看到$('#id_industry') 时,这指的是行业选择器ID,与$('#id_sector') 指的是行业选择器ID 相同

我称之为 AJAX 函数进入 HTML 模板

我所说的 AJAX 视图 放在 .py 文件中,我通常将 AJAX 视图添加到 ajax.py 文件中 对于这个例子,我在 AJAX 视图 中添加了@csrf_exempt,如果您想知道如何在 AJAX 函数中管理 CSRF 令牌 ,你可以去:CSRF & AJAX: Official Docs 在 AJAX 视图中有用户选择的 Sector: 您需要生成行业列表 该列表将用于生成 select 的选项 您可以通过多种不同的方式执行此操作,您应该选择更适合您的场景的一种。 我正在发送 ID,但如果对您更有利,您可以发送姓名

编辑:过滤部门/行业

get_industries(request)改为get_sectors(request)

首先你应该有等价物,你可以用不同的方式拥有它们,你可以有 2 个模型 Industry 和 Sector,并使用外键关联它们,但我认为你没有模型,所以你可以有 2 个字典,一种用于行业,一种用于行业。

我会假设等价,您应该将 dict 的值更改为您需要的值:

INDUSTRY_DICT = 
4: range(14,36),
5: range(36,58),
6: range(58,80),
7: range(80,102),
8: range(102,124),
10: range(124,146)  # This is the only equivalence that you passed to me

这将生成一个字典,如果您执行INDUSTRY_DICT[industry_id],它将返回应该出现在选择中的扇区的 ID 列表。检查上面的函数是否有新的变化。

我还建议您再使用 2 个函数来“重新启动”两个选择并再次附加所有可能的选项,以防用户想要更改他的选择

【讨论】:

Liarez,感谢您提供如此详细的答案。不知道如何编写代码: # 在这里您应该添加代码以获取行业 # 我假设您将使用部门来过滤行业 # 您应该在变量“行业”中获取行业列表。我不确定如何获取行业列表然后过滤行业。如果您能为我提供答案,我可以提出赏金问题。 @user3354539 你应该在这部分做的是过滤基于一个部门的行业。如果您告诉我使用一个部门过滤行业的逻辑,反之亦然,我可以完成该功能 Liarez,如果用户选择行业值 10 - Engineering,则应填充行业选择列表的行业是 124 到 145 的行业值。我不确定如何关联这两个值(10和 124-145) 在一起。 @user3354539 好的!给我几分钟,我会添加剩下的部分,我只是添加一个小例子,你应该多实现一点,但你会知道怎么做 @user3354539 检查答案底部的编辑,以及函数 get_sectors 的更改

以上是关于django & ajax 依赖的 html 选择列表(级联下拉列表)的主要内容,如果未能解决你的问题,请参考以下文章

Django动态表与AJAX

HTML&javaSkcript&CSS&jQuery&ajax

django,ajax:如何有效地实时更新一堆数据

Ajax jQuery serialize() & serializeArray() textarea 未以 Django 形式提交

Django + Axios & Ajax post和get 传参

如何格式化 Django URL,以便 AJAX 调用将获取不依赖于当前页面但来自表单值的数据库数据?