【问题标题】:Dynamic FilteredSelectMultiple in django-admindjango-admin 中的动态 FilteredSelectMultiple
【发布时间】:2011-11-23 14:36:27
【问题描述】:

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

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 的形式。这个想法是,当您选择incidence 中的instalation 时,只有与该instalation 相关的machines 可供选择。我想这是不可能的:

queryset=Machine.objects.filter(instalation=self.instalation).order_by('hostname'),

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

【问题讨论】:

    标签: django django-models django-admin


    【解决方案1】:

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

    我想出了这个解决方案:

    • 将“select”列表包装在另一个元素中(例如“div”)
    • 使用从 ajax 调用收到的数据重新创建原始列表
    • 调用“SelectFilter.init”重新构造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】:

      您可以在保存模型后执行此操作,并且有一个与之关联的instalation 可供使用(尽管查找将是instalation=self.instance.instalation)。

      但是,这对您没有多大好处,因为如果选择了不同的 instalation,则列表仍将是旧选择的列表,显然您在第一次创建对象时没有任何帮助。

      因此,实现此目的的唯一方法是使用 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);
      

      【讨论】:

      • 谢谢!感觉没那么容易,反正我会试试的!
      【解决方案3】:
      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 字段。

      【讨论】:

        猜你喜欢
        • 2011-06-19
        • 2016-03-13
        • 2015-05-06
        • 2021-09-15
        • 2021-10-13
        • 2020-07-03
        • 2011-12-21
        • 2011-05-16
        • 2018-03-11
        相关资源
        最近更新 更多