最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Custom Dependent Dropdown menu inside the Django admin - Stack Overflow

programmeradmin7浏览0评论

I have a project foreign key in by Phase model. I'm having hard time Create a dependent drop-down list inside my Django admin page.

I want to when user select a project from (project drop-down) phase of that project show in second dop-down

What would be the best way to achieve this?

It would be great if the dropdowns filter items based on the value of its parent.

class Project(models.Model):
    name                    = models.CharFieldmax_length = 100, unique= True)
    short_name              = models.CharField(max_length= 4, unique= True)
    slug                    = models.SlugField(max_length= 100, allow_unicode=True, null=True, editable= False)
    location                = models.OneToOneField(Location, on_delete = models.SET_NULL, null= True, blank= False, verbose_name= 'موقعیت')
    start_date              = models.DateField(default= timezone.now, null= True, blank= True)      
    end_date                = models.DateField(default= timezone.now, null= True, blank= True)
    duration                = models.IntegerField(default= 0, editable= False)

class Phase(models.Model):
    title                = models.CharField(max_length= 20)

class ProjectPhase(models.Model):
    project                 = models.ForeignKey(Project, on_delete= models.CASCADE, related_name= 'phase')
    phase                   = models.ForeignKey(Phase, on_delete=models.CASCADE, related_name= 'project')
    start_date              = models.DateField(default= timezone.now)      
    end_date                = models.DateField(default= timezone.now)
    duration                = models.IntegerField(default= 0, editable= True)

I have a project foreign key in by Phase model. I'm having hard time Create a dependent drop-down list inside my Django admin page.

I want to when user select a project from (project drop-down) phase of that project show in second dop-down

What would be the best way to achieve this?

It would be great if the dropdowns filter items based on the value of its parent.

class Project(models.Model):
    name                    = models.CharFieldmax_length = 100, unique= True)
    short_name              = models.CharField(max_length= 4, unique= True)
    slug                    = models.SlugField(max_length= 100, allow_unicode=True, null=True, editable= False)
    location                = models.OneToOneField(Location, on_delete = models.SET_NULL, null= True, blank= False, verbose_name= 'موقعیت')
    start_date              = models.DateField(default= timezone.now, null= True, blank= True)      
    end_date                = models.DateField(default= timezone.now, null= True, blank= True)
    duration                = models.IntegerField(default= 0, editable= False)

class Phase(models.Model):
    title                = models.CharField(max_length= 20)

class ProjectPhase(models.Model):
    project                 = models.ForeignKey(Project, on_delete= models.CASCADE, related_name= 'phase')
    phase                   = models.ForeignKey(Phase, on_delete=models.CASCADE, related_name= 'project')
    start_date              = models.DateField(default= timezone.now)      
    end_date                = models.DateField(default= timezone.now)
    duration                = models.IntegerField(default= 0, editable= True)
Share Improve this question edited May 3, 2020 at 8:09 BABZI asked May 3, 2020 at 8:02 BABZIBABZI 1194 silver badges9 bronze badges
Add a ment  | 

2 Answers 2

Reset to default 5

1. import a js media file in ModelAdmin for Generaldata:

class YourModelAdmin(admin.ModelAdmin):    
    form = YourModelForm
    #list_display = ['your fields',]
    class Media:
        js = ("yourapp/selectajax.js",)

admin.site.register(YourModel, YourModelAdmin)

2. create a new js file which saved yourproject/yourapp/static/yourapp/ directory or another proper directory.

jQuery(function($){
    $(document).ready(function(){
        $("#id_project_select").change(function(){
            // console.log(obj.currentTarget.value);
            $.ajax({
                url:"/get_phases/",
                type:"POST",
                data:{project: $(this).val(),},
                success: function(result) {
                    console.log(result);
                    cols = document.getElementById("id_phase_select");
                    cols.options.length = 0;
                    for(var k in result){
                        cols.options.add(new Option(k, result[k]));
                    }
                },
                error: function(e){
                    console.error(JSON.stringify(e));
                },
            });
        });
    }); 
});

3. create a view to process ajax

@login_required
def get_phases(request):
    project = request.POST.get('project')
    phases = {}
    try:
        if project:
            prophases = Project.objects.get(pk=int(project)).phase
            phases = {pp.phase.title:pp.pk for pp in prophases}
    except:
        pass
    return JsonResponse(data=phases, safe=False)

4. add 'get_phases/ to urlpatterns.

Notice that you should modify some codes as your need.

The answer by Blackdoor is a good approach and it's the one we just implemented, but it has a couple of problems:

  1. It's only executed when you change the main select, and I wanted the dependant select to be filtered also on page load.
  2. Does not keep que selected item in the dependant select.

In his solution, in step 2, replace his code with this one and adapt the names (I'm using service and sub_service instead of project / phase):

jQuery(function($){
    $(document).ready(function(){
        var clone = document.getElementById("id_sub_service").cloneNode(true);
        $("#id_service").change(function(){
            update_sub_services($(this).val(), clone)
        });
        update_sub_services($("#id_service").val(), clone)
    });

    function update_sub_services(service, clone) {
        $.ajax({
            url:"/chained_dropdowns/get_sub_services/",
            type:"GET",
            data:{service: service,},
            success: function(result) {
                var cols = document.getElementById("id_sub_service");
                cols.innerHTML = clone.innerHTML
                Array.from(cols.options).forEach(function(option_element) { 
                    var existing = false;
                    for (var k in result) {
                        if (option_element.value == k) {
                            existing = true
                        }
                    }
                    if (existing == false) {
                        $("#id_sub_service option[value='"+option_element.value+"']").remove();
                    }
                })
            },
            error: function(e){
                console.error(JSON.stringify(e));
            },
        });
    }
});

As you can see, now instead of removing all the items from the dependant select and then refilling it (which leaves you without the selected property and any other custom property), it removes the options that should not be there.

I'm not a JS developer and I don't know jQuery so my modifications are in native JS, please feel free to improve it :)

发布评论

评论列表(0)

  1. 暂无评论