如何从客户端输入中过滤 ModelAdmin autocomplete_fields 结果

How to filter ModelAdmin autocomplete_fields results from clientside input

这是这个话题的后续: How to filter ModelAdmin autocomplete_fields results with the context of limit_choices_to

Uberdude 提出了一个解决方案,它非常适合根据触发请求的字段自定义自动选择查询集,但我还需要根据来自客户端的输入进行过滤,最具体地说是一个不是模型的复选框字段,并且仅适用于此表单摘录中的表单中的某些字段。

我通过覆盖您的 AutocompleteSelect 小部件成功地将复选框应用于小部件:

class AutocompleteSelectCb(AutocompleteSelect):
def render(self, name, value, attrs=None):
    s = super().render(name, value, attrs)
    return mark_safe('<div style="margin-bottom:10px;"><input type="checkbox" id="parent1"\
         name="parentx" value="1">Search among all kennels</div>' + s)

并且仅当字段出现在管理员的 autocomplete_cb_fields 属性中时才使用该小部件:

 autocomplete_fields = ['breed']
 autocomplete_cb_fields = ['father', 'mother']

但是,我不确定如何让我的 AutocompleteSelectCb 小部件发送复选框的状态,以便它可以在 get_search_results 方法中进行处理。我假设有一些 js,但是如何?有什么想法吗?

我终于能够使用 post 中列出的方法解决问题,即向引用 URL 添加查询字符串键,因为 select2 不接受修改初始化后的请求 URL。 我还添加了有关正在编辑的狗的其他信息,例如它的 ID 和出生年份,以进一步过滤查询集(狗不能是自己的 parent 或有比自己年轻的 parent,母亲只能是女性...)。

//this get passes to the select2:opening event listener
function expand_ajax_location_search($, fieldId) {
    if (!$) {
        $ = django.jQuery;
    }
    var pageURL = $(location).attr("href");
    var match = pageURL.match(/\/.*\/(\d+)\/change/);
    return `&birth_year=${$('#id_birth_year').val()}&dog_id=${match[1]}&father_cb=${$('#father-cb').prop( "checked" )}&mother_cb=${$('#mother-cb').prop( "checked" )}`
}

最后在服务器端进行过滤具有挑战性,因为狗管理员通常由狗舍过滤,因此每个狗舍所有者只能编辑他们的狗,尽管复选框允许他们使用其他狗舍的狗作为 parent秒。 get_search_results 方法中的挑战是在复选框被选中时取消过滤查询集,这是不可能完成的,因此在 将其传递给 super() 之前创建一个新的 。get_search_results。 此外,基础 get_search_results 方法似乎会遍历所有关系,即使不需要它们也会导致超过 100 个查询,因此对所有关系使用 select_related 只会产生一个查询。

def get_search_results(self, request, queryset, search_term):
    if request.is_ajax and '/autocomplete/' in request.path and request.GET.get('model_name') == 'dog':
        url = urllib.parse.urlparse(request.headers['Referer'])
        referer = url.path
        # (parse_qs results are lists)
        qs = urllib.parse.parse_qs(url.query)
        if request.GET.get('field_name') == 'father':
            if qs.get('father_cb')[0] == 'true':
                # default is filetered by kennel so need new qs
                queryset = Dog.objects.select_related(
                    'kenn', 'img', 'father', 'mother', 'breeder').order_by('name')
            queryset, use_distinct = super().get_search_results(request, queryset, search_term)
            queryset = queryset.exclude(
                sex='F').exclude(id=qs.get('dog_id')[0])
        elif request.GET.get('field_name') == 'mother':
            if qs.get('mother_cb')[0] == 'true':
                # default is filetered by kennel so need new qs
                queryset = Dog.objects.select_related(
                    'kenn', 'img', 'father', 'mother', 'breeder').order_by('name')
            queryset, use_distinct = super().get_search_results(request, queryset, search_term)
            queryset = queryset.exclude(
                sex='M').exclude(id=qs.get('dog_id')[0])
        birth_year = qs.get('birth_year')[0]
        if birth_year:
            queryset = queryset.exclude(birth_year__gt=birth_year)
    else:
        queryset, use_distinct = super().get_search_results(request, queryset, search_term)
    return queryset, use_distinct