开发者

Django admin search: how to override the default handler?

I wish to customize the way in which search queries across the search_fields.

Is there a way to do it without hacking deeply into the Django code or creating a totally independent view?

For instance, I would开发者_运维百科 like to return the union of the querysets for each of the items of the querystring.split(). So that searching for "apple bar" would return results with EITHER apple OR bar, unlike the default search which applies an AND operator.


It is very easy to do this in django 1.6

ModelAdmin.get_search_results(request, queryset, search_term) New in Django 1.6.

import operator
# from django.utils.six.moves import reduce  # if Python 3
from django.db.models import Q

class PersonAdmin(admin.ModelAdmin):
    list_display = ('name', 'age')
    search_fields = ('name',)

    def get_search_results(self, request, queryset, search_term):
        # search_term is what you input in admin site
        # queryset is search results
        queryset, use_distinct = super(PersonAdmin, self).get_search_results(request, queryset, search_term)

        search_term_list = search_term.split(' ')#['apple','bar']
        # you can also use `self.search_fields` instead of following `search_columns`
        search_columns = ('name','age','address')
        #convert to Q(name='apple') | Q(name='bar') | Q(age='apple') | ...
        query_condition = reduce(operator.or_, [Q(**{c:v}) for c in search_columns for v in search_term_list])

        queryset = self.model.objects.filter(query_condition)
        # NOTICE, if you want to use the query before
        # queryset = queryset.filter(query_condition)
        return queryset, use_distinct


So I have been using the code from WeizhongTu's answer and found a not-so-obvious error in it. When we try to use both filtering and searching with this code, filtering is shadowed by this line:

queryset = self.model.objects.filter(eval(query_condition))

It is important to use the previous results ONLY. So you must never use self.model.objects to obtain the queryset, but only filter the queryset itself. Like this:

def get_search_results(self, request, queryset, search_term):
    # search_term is what you input in admin site
    # queryset is the list of objects passed to you
    # by the previous functions, e. g. filtering 
    search_term_list = search_term.split(' ') #['apple','bar']
    search_columns = ('name','age','address')
    # convert to Q(name='apple') | Q(name='bar') | Q(age='apple') | ...
    query_condition = ' | '.join(['Q(%s="%s")'%(x,y) for x in search_term_list for y in search_columns])
    appended_queryset = queryset.filter(eval(query_condition))
    # queryset is search results
    queryset, use_distinct = super(PersonAdmin, self).get_search_results(request, queryset, search_term)
    queryset |= appended_queryset
    return queryset, use_distinct


you can add an ModelAdmin method:

def queryset(self, request):
    qs = super(MyModelAdmin, self).queryset(request)
    # modify queryset here, eg. only user-assigned tasks
    qs.filter(assigned__exact=request.user)
    return qs

you have a request here, so most of the stuff can be view dependent, including url parameters, cookies, sessions etc.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜