text: # encoding: utf-8
  import operator
  from functools import reduce
  
  from django.contrib.admin.utils import lookup_needs_distinct
  from django.core.exceptions import FieldDoesNotExist
  from django.db.models import Q
  from django.db.models.constants import LOOKUP_SEP
  
  
  def split_camel_name(name, fall=False):
      """
      驼峰命名分割为单词
  
      GenerateURLs => [Generate, URLs]
      generateURLsLite => [generate, URLs, Lite]
      """
      if not name:
          return []
  
      lastest_upper = name[0].isupper()
      idx_list = []
      for idx, char in enumerate(name):
          upper = char.isupper()
          # rising
          if upper and not lastest_upper:
              idx_list.append(idx)
          # falling
          elif fall and not upper and lastest_upper:
              idx_list.append(idx-1)
          lastest_upper = upper
  
      l_idx = 0
      name_items = []
      for r_idx in idx_list:
          if name[l_idx:r_idx]:
              name_items.append(name[l_idx:r_idx])
          l_idx = r_idx
      if name[l_idx:]:
          name_items.append(name[l_idx:])
  
      return name_items
  
  
  def construct_search(queryset, field_name):
      """
      生成搜索关键字
      """
      if field_name.startswith('^'):
          return "%s__istartswith" % field_name[1:]
      elif field_name.startswith('='):
          return "%s__iexact" % field_name[1:]
      # Use field_name if it includes a lookup.
      opts = queryset.model._meta
      lookup_fields = field_name.split(LOOKUP_SEP)
      # Go through the fields, following all relations.
      prev_field = None
      for path_part in lookup_fields:
          if path_part == 'pk':
              path_part = opts.pk.name
          try:
              field = opts.get_field(path_part)
          except FieldDoesNotExist:
              # Use valid query lookups.
              if prev_field and prev_field.get_lookup(path_part):
                  return field_name
          else:
              prev_field = field
              if hasattr(field, 'get_path_info'):
                  # Update opts to follow the relation.
                  opts = field.get_path_info()[-1].to_opts
      # Otherwise, use the field with icontains.
      return "%s__icontains" % field_name
  
  
  def get_search_results(queryset, search_term, search_fields, model):
      """
      Return a tuple containing a queryset to implement the search
      and a boolean indicating if the results may contain duplicates.
      """
      use_distinct = False
      if search_fields and search_term:
          orm_lookups = [construct_search(queryset, str(search_field)) for search_field in search_fields]
          for bit in search_term.split():
              or_queries = [Q(**{orm_lookup: bit}) for orm_lookup in orm_lookups]
              queryset = queryset.filter(reduce(operator.or_, or_queries))
          use_distinct |= any(lookup_needs_distinct(model._meta, search_spec) for search_spec in orm_lookups)
      return queryset, use_distinct
  