【问题标题】:Django Admin change_list filtering multiple ManyToManyDjango Admin change_list 过滤多个ManyToMany
【发布时间】:2014-02-03 19:47:56
【问题描述】:

在 Django-Admin 中,您可以在模型的字段上定义 list_filter。这也适用于多对多字段。

class ModelA(models.Model):
    name = models.CharField(max_length=100, verbose_name="Name")

class ModelB(models.Model):
    model_a_relation = models.ManyToManyField(ModelA)

class ModelBAdmin(ModelAdmin):
    list_filter = [model_a_relation, ]

admin.site.register(ModelB, ModelBAdmin)

现在,我可以在 ModelB 的 Admin object_list 中通过与 ModelA 的关系过滤我的 ModelB 元素列表。

现在我的问题是:是否可以按 ModelA 的多个对象进行过滤?

在 ModelB 的 change_view 中,我使用 django-autocomplete-light 来定义关系。我也可以使用这个小部件来过滤 change_list 吗?

我想像ModelB.objects.filter(model_a_relation__in=names) 这样的过滤器后台查询,其中names 是ModelA 所选对象的列表。

谢谢,霍斯特

【问题讨论】:

    标签: django django-admin django-admin-filters django-autocomplete-light


    【解决方案1】:

    我做了一个非常肮脏的尝试来解决我的问题。它有效,我想与你分享。

    为了更好地理解我在这个例子中使用了新的模型:

    class Tag(models.Model):
        name = models.CharField(max_length=100, verbose_name="Name")
    
    class Book(models.Model):
        tags = models.ManyToManyField(Tag)
        read = models.BooleanField()
    
    class BookAdmin(ModelAdmin):
        list_filter = ['read', ]
    
    admin.site.register(Book, BookAdmin)
    

    起初,我覆盖了 BookAdmin 的changelist_view

    def changelist_view(self, request, extra_context=None):
    
        extra_context = extra_context if extra_context else {}
    
        q = request.GET.copy()
    
        tags = Tag.objects.all().values('id', 'name')
    
        current_tags = q.get('tags__id__in', [])
        tag_query = request.GET.copy()
        if current_tags:
            tag_query.pop('tags__id__in')
            current_tags = current_tags.split(',')
            all_tag = False
        else:
            all_tag = True
        for tag in tags:
            if str(tag['id']) in current_tags:
                tag['selected'] = True
                temp_list = list(current_tags)
                temp_list.remove(str(tag['id']))
                tag['tag_ids'] = ','.join(temp_list)
            else:
                tag['selected'] = False
                tag['tag_ids'] = ','.join(current_tags)
    
        extra_context['tag_query'] = '?' if len(tag_query.urlencode()) == 0 else '?' + tag_query.urlencode() + '&'
        extra_context['all_tag'] = all_tag
        extra_context['tags'] = tags
    
        return super(BookAdmin, self).changelist_view(request, extra_context=extra_context)
    

    如您所见,我在 GET 中查看是否选择了一些标签。然后我为每个可能的标签构建新的 GET 参数。

    然后是我覆盖的 change_list.html

    {% extends "admin/change_list.html" %}
    
    {% block content %}
        {{ block.super }}
    
        <h3 id="custom_tag_h3"> Fancy Tag filter</h3>
        <ul id="custom_tag_ul">
    
            <li{% if all_tag %} class="selected"{% endif %}>
                <a href="{{ tag_query }}">All</a>
            </li>
    
            {% for tag in tags %}
                <li{% if tag.selected %} class="selected"{% endif %}>
                    <a href="{{ tag_query }}tags__id__in={{ tag.tag_ids }}{% if not tag.selected %}{% if tag.tag_ids %},{% endif %}{{ tag.id }}{% endif %}">{{ tag.name }}</a>
                </li>
            {% endfor %}
    
        </ul>
    
        <script type="text/javascript">
            $('#changelist-filter').append($('#custom_tag_h3'));
            $('#custom_tag_h3').after($('#custom_tag_ul'));
        </script>
    
    {% endblock content %}
    

    这样我就有了一个看起来像布尔值read-filter 的过滤器,我可以在其中激活多个选项。通过单击过滤器,一个新的 id 被添加到查询中。再次单击已选择的选项会从查询中删除 id。点击 All 会从 URL 中删除 tags_in-parameter 漏洞。

    【讨论】:

    • 如果 django-autocomplete-light 要在开箱即用的更改列表过滤器中提供自动完成支持,它可以显示路径!
    • 非常感谢您!这正是我在 SimpleListFilter 界面中所缺少的。
    【解决方案2】:

    允许用户在更改列表视图中使用自动完成的另一个选项:

    • 设置ModelAdmin.search_fields 在自动完成提供的字段中搜索,即。

      class ModelBAdmin(ModelAdmin):
          search_fields = ['model_a_relation__name']
      
    • override the changelist tomplatespawn an autocomplete on the search input:

      <script type="text/javascript">
      $(document).ready(function() {
          $('#searchbar').yourlabsAutocomplete({
              url: '{% url 'autocomplete_light_autocomplete' 'YourAutocompleteName' %}',
              choiceSelector: '[data-value]',
          }).input.bind('selectChoice', function(e, choice, autocomplete) {
              $(this).val(choice.text())
                     .parents('form').submit();
          });
      });
      </script>
      
    • 确保添加此脚本后没有JS错误!

    【讨论】:

    • 非常感谢您的想法,但我不想关注自动完成,而是关注多选过滤的可能性。此外,我已经为 ModelB 的其他属性提供了一个自动完成功能的搜索栏,并且不希望它们与这些新东西混在一起。我想在正确的过滤区域中使用自动完成小部件。
    猜你喜欢
    • 2017-01-15
    • 2011-09-10
    • 2017-01-15
    • 2018-01-12
    • 1970-01-01
    • 1970-01-01
    • 2017-02-10
    • 1970-01-01
    • 2014-01-30
    相关资源
    最近更新 更多