django-admin中的Dynamic FilteredSelectMultiple

时间:2011-11-23 14:36:28

标签: django django-models django-admin

我不知道这是否可行,无论如何,我目前有以下内容:

class Incidence(models.Model):
    ...
    instalation = models.ForeignKey('Instalation')
    machine = models.ManyToManyField('Machine')
    ...

class Machine(models.Model):
    ...
    instalation = models.ForeignKey('Instalation')
    ...

因此Machines属于instalationsincidencesmachinesincidences相关,其目的是设置动态FilteredSelectMultiple小部件选择与管理页面中的machines相关的incidence。管理员目前是:

class IncidenceMachineForm(forms.ModelForm):
    filtered_machine = ModelMultipleChoiceField(
        queryset=Machine.objects.order_by('hostname'),
        required=False, widget=FilteredSelectMultiple("filtered machine name", is_stacked=False)
    )
    class Meta:
        model = Incidence

然后,modelAdmin使用IncidenceMachineForm形式。我们的想法是,当您选择instalation的{​​{1}}时,只有与incidence相关的machines可供选择。我想这是不可能的事情:

instalation

任何想法都将受到高度赞赏。谢谢!

3 个答案:

答案 0 :(得分:1)

你可以在保存模型后执行,并且有一个instalation与之关联(尽管查找将是instalation=self.instance.instalation)。

然而,这对你没有多大帮助,因为如果选择了不同的instalation,那么列表仍然是旧选择的列表,显然你在第一次创建对象时没有任何帮助。 / p>

因此,实现这一目标的唯一方法是使用AJAX。您创建一个视图以接收选定的instalation ID,并返回由与之关联的machines组成的JSON响应。将视图绑定到您的urlconf,然后使用AJAX点击它并根据结果更新选择框。

from django.http import Http404, HttpResponse
from django.shortcuts import get_object_or_404
from django.utils import simplejson

def ajax_admin_get_machines_for_instalation(request):
    instalation_id = request.GET.get('instalation_id')
    if instalation_id is None:
        # instalation_id wasn't provided so return all machines
        machines_qs = Machine.objects.all()
    else:
        instalation = get_object_or_404(Instalation, pk=instalation_id)
        machines_qs = Machine.objects.filter(instalation=instalation)

    # 'name' is the field you want to use for the display value
    machines = machines_qs.values('pk', 'name')

    return HttpResponse(simplejson.dumps(machines), mimetype='application/json')

然后JS:

(function($){
    $(document).ready(function(){
        function update_machine_options(){
            var selected = $('#id_instalation').val();
            if (selected) {
                $.getJSON('/url/for/ajax/view/', {
                    instalation_id: selected
                }, function(data, jqXHR){
                    var options = [];
                    for (k in data) {
                        options.append('<option value="'+data[k].pk+'">'+data[k].name+'</option>');
                    }
                    $('#id_machine').html(options.join(''));
                });
            }
        }

        update_machine_options();
        $('#id_instalation').change(function(){
            update_machine_options();
        });
    });
})(django.jQuery);

答案 1 :(得分:1)

我注意到FilteredSelectMultiple小部件在页面加载后已经缓存,转换并更改了原始小部件的名称,因此更改了&#34;选项&#34; &#34;选择&#34;标签是不够的。

我提出了这个解决方案:

  • 包装&#34;选择&#34;在另一个元素中列出(&#34; div&#34;例如)
  • 使用从ajax调用收到的数据重新创建原始列表
  • call&#34; SelectFilter.init&#34;重新构建FilteredSelectMultiple小部件

以下是我测试的代码:

$('#id_instalation').change(function() {
    var selected = $('#id_instalation').val();
    if(selected) {
        $.ajax({
            url: '/url/to/get/machines/' + selected,
            success: function(list) {
                var options = [];
                options.push('<select multiple="multiple" class="selectfilter" name="machine" id="id_machine">');
                for(i in list){
                    options.push('<option value="' + list[i][0] + '">' +
                        list[i][1] + '</option>');
                }
                options.push('</select>');
                $('#machine_wrapper').html(options.join(''));

                // Change title of widget
                var title = $('#id_instalation option:selected"').text().toLowerCase();
                SelectFilter.init("id_machine", title, 0, "/path/to/django/media/");
            },
            error: function() {
                alert('Server error');
            },
        });
    }
}

这是从ajax调用返回的数据样本:

[[1, "Machine 1"], [2, "Machine 2"], [3, "Machine 3"]]

对于服务器端实现,请参阅Chris Pratt的回答

注意:测试时:

  • 的jquery-1.7.2
  • django 1.2.5

答案 2 :(得分:0)

from django.contrib.admin.widgets import FilteredSelectMultiple    

@admin.register(YourModel)
    class YourModelAdmin(admin.ModelAdmin):

        def formfield_for_manytomany(self, db_field, request, **kwargs):
            kwargs['widget'] = FilteredSelectMultiple(
                db_field.verbose_name,
                False,
            )
            return super().formfield_for_manytomany(db_field, request, **kwargs)

快速,不需要覆盖ModelForm等。 影响所有m2m字段。