【问题标题】:Django admin search: how to allow OR operator between search terms?Django 管理员搜索:如何在搜索词之间允许 OR 运算符?
【发布时间】:2011-03-31 13:58:58
【问题描述】:

django-sphinx 似乎有点矫枉过正。

添加此类功能的最简单方法是什么?

谢谢

【问题讨论】:

  • 因此,当有人搜索“这里有东西”时,您是否希望您的查询是“...其中 x 喜欢 '%here%' 或 x 喜欢 '%is%' 或 x 喜欢 ' %something%'"?
  • 不,我的意思是“这里 OR 是 OR 某事”的查询会变成...

标签: django search full-text-search django-admin


【解决方案1】:

对于 Django 1.8(或者可能更早),这已经改变了。这对我有用:

class MyAdmin(admin.ModelAdmin):

    def get_search_results(self, request, queryset, search_term):
        """
        Returns a tuple containing a queryset to implement the search,
        and a boolean indicating if the results may contain duplicates.
        """
        # Apply keyword searches.
        def construct_search(field_name):
            if field_name.startswith('^'):
                return "%s__istartswith" % field_name[1:]
            elif field_name.startswith('='):
                return "%s__iexact" % field_name[1:]
            elif field_name.startswith('@'):
                return "%s__search" % field_name[1:]
            else:
                return "%s__icontains" % field_name

        use_distinct = False
        search_fields = self.get_search_fields(request)

        # starts here
        filters = models.Q()

        if search_fields and search_term:
            orm_lookups = [construct_search(str(search_field))
                           for search_field in search_fields]
            for bit in search_term.split():
                or_queries = [models.Q(**{orm_lookup: bit})
                              for orm_lookup in orm_lookups]

                # this | operation of Q()'s is the ticket.
                filters = filters | models.Q(reduce(operator.or_, or_queries))

            if not use_distinct:
                for search_spec in orm_lookups:
                    if admin.utils.lookup_needs_distinct(self.opts, search_spec):
                        use_distinct = True
                        break

        # finally
        queryset = queryset.filter(filters)

        return queryset, use_distinct

【讨论】:

  • 您的代码的最后一点似乎在break 语句之后重复。对于 Python3,我还需要添加 from functools import reduce,因为它被移到了那里。否则它似乎工作得很好!
  • 啊,我已经清理了那些多余的语句——谢谢 Jarno!
【解决方案2】:

这是一个更完整的sn-p:

from django.contrib.admin.views.main import ChangeList
from django.db import models
import operator

class MyChangeList(ChangeList):
    def __init__(self, *a):
        super(MyChangeList, self).__init__(*a)
    def get_query_set(self, request):
        # First, we collect all the declared list filters.
        (self.filter_specs, self.has_filters, remaining_lookup_params,
         use_distinct) = self.get_filters(request)

        # Then, we let every list filter modify the queryset to its liking.
        qs = self.root_query_set
        for filter_spec in self.filter_specs:
            new_qs = filter_spec.queryset(request, qs)
            if new_qs is not None:
                qs = new_qs

        try:
            # Finally, we apply the remaining lookup parameters from the query
            # string (i.e. those that haven't already been processed by the
            # filters).
            qs = qs.filter(**remaining_lookup_params)
        except (SuspiciousOperation, ImproperlyConfigured):
            # Allow certain types of errors to be re-raised as-is so that the
            # caller can treat them in a special way.
            raise
        except Exception, e:
            # Every other error is caught with a naked except, because we don't
            # have any other way of validating lookup parameters. They might be
            # invalid if the keyword arguments are incorrect, or if the values
            # are not in the correct type, so we might get FieldError,
            # ValueError, ValidationError, or ?.
            raise IncorrectLookupParameters(e)

        # Use select_related() if one of the list_display options is a field
        # with a relationship and the provided queryset doesn't already have
        # select_related defined.
        if not qs.query.select_related:
            if self.list_select_related:
                qs = qs.select_related()
            else:
                for field_name in self.list_display:
                    try:
                        field = self.lookup_opts.get_field(field_name)
                    except models.FieldDoesNotExist:
                        pass
                    else:
                        if isinstance(field.rel, models.ManyToOneRel):
                            qs = qs.select_related()
                            break

        # Set ordering.
        ordering = self.get_ordering(request, qs)
        qs = qs.order_by(*ordering)

        # Apply keyword searches.
        def construct_search(field_name):
            if field_name.startswith('^'):
                return "%s__istartswith" % field_name[1:]
            elif field_name.startswith('='):
                return "%s__iexact" % field_name[1:]
            elif field_name.startswith('@'):
                return "%s__search" % field_name[1:]
            else:
                return "%s__icontains" % field_name

        if self.search_fields and self.query:
            orm_lookups = [construct_search(str(search_field))
                           for search_field in self.search_fields]
            or_queries = []
            for bit in self.query.split():
                or_queries += [models.Q(**{orm_lookup: bit})
                              for orm_lookup in orm_lookups]
            if len(or_queries) > 0:
                qs = qs.filter(reduce(operator.or_, or_queries))
            if not use_distinct:
                for search_spec in orm_lookups:
                    if lookup_needs_distinct(self.lookup_opts, search_spec):
                        use_distinct = True
                        break

        if use_distinct:
            return qs.distinct()
        else:
            return qs

在你的 ModelAdmin 中

def get_changelist(*a, **k):
    return MyChangeList

【讨论】:

  • 您应该使用真实的用户名并更新您的个人资料,然后您的贡献将得到更认真的对待。
【解决方案3】:

您可以从这里开始:

  1. 子类化 django.contrib.admin.views.main.ChangeList 管理视图类,覆盖 get_query_set 方法以返回考虑到“OR”关键字的查询集。

  2. 告诉您的 ModelAdmin 类使用您的新 ChangeList 子类:

在您的admin.py 文件中:

from django.contrib.admin.views.main import ChangeList

class YourChangeList(ChangeList)
    def get_query_set(self):
        # I'll leave this part to you...

class YourAdminClass(admin.ModelAdmin):
    def __init__(self):
        super(YourAdminClass, self).__init__()
        self.changelist_view = YourChangeListClass

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-02-17
    • 2013-01-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-06
    相关资源
    最近更新 更多