2010-08-18 75 views
4

django-sphinx似乎有點矯枉過正。Django管理員搜索:如何讓搜索詞之間的OR運算符?

添加此類功能的最簡單方法是什麼?

感謝

+0

所以當有人搜索這「這是什麼」你想你的查詢是「...其中x像'%here%'或x像'%is%'或x像'%something%'「? – 2010-08-18 21:15:59

+0

不,我的意思是一個查詢「這裏或是某個東西」會變成那個...... – 2010-08-19 10:21:09

回答

0

在這裏你可能會開始:

  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 
2

下面是一個更完整的片段:

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 
+0

您應該使用真實的用戶名並更新您的配置文件,那麼您的貢獻將會更加嚴肅。 – nalply 2012-10-05 18:09:55

2

這改變了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 
+0

你的代碼的最後一部分似乎在'break'語句後重復。對於Python3,我還需要添加'from functools import reduce',因爲它已經移到那裏。否則,它似乎很好! – Jarno 2016-04-26 12:16:35

+0

啊,我已經清理了那些額外的陳述 - 謝謝雅諾! – bahoo 2016-04-27 20:24:56