def get_filters(self, request): lookup_params = self.get_filters_params() use_distinct = False for key, value in lookup_params.items(): if not self.lookup_allowed(key, value): raise DisallowedModelAdminLookup( "Filtering by %s not allowed" % key) filter_specs = [] if self.list_filter: for list_filter in self.list_filter: if callable(list_filter): # This is simply a custom list filter class. spec = list_filter(request, lookup_params, self.model, self.model_admin) else: field_path = None if isinstance(list_filter, (tuple, list)): # This is a custom FieldListFilter class for a given # field. field, field_list_filter_class = list_filter else: # This is simply a field name, so use the default # FieldListFilter class that has been registered for # the type of the given field. field = list_filter field_list_filter_class = FieldListFilter.create if not isinstance(field, models.Field): field_path = field field = get_fields_from_path(self.model, field_path)[-1] spec = field_list_filter_class(field, request, lookup_params, self.model, self.model_admin, field_path=field_path) # Check if we need to use distinct() use_distinct = (use_distinct or lookup_needs_distinct( self.opts, field_path)) if spec and spec.has_output(): filter_specs.append(spec) # At this point, all the parameters used by the various ListFilters # have been removed from lookup_params, which now only contains other # parameters passed via the query string. We now loop through the # remaining parameters both to ensure that all the parameters are valid # fields and to determine if at least one of them needs distinct(). If # the lookup parameters aren't real fields, then bail out. try: for key, value in lookup_params.items(): lookup_params[key] = prepare_lookup_value(key, value) use_distinct = (use_distinct or lookup_needs_distinct(self.opts, key)) return (filter_specs, bool(filter_specs), lookup_params, use_distinct) except FieldDoesNotExist as e: raise IncorrectLookupParameters from e
def get_filters(self, request): lookup_params = self.get_filters_params() use_distinct = False # Normalize the types of keys for key, value in lookup_params.items(): if not isinstance(key, str): # 'key' will be used as a keyword argument later, so Python # requires it to be a string. del lookup_params[key] lookup_params[force_str(key)] = value if not self.model_admin.lookup_allowed(key, value): raise DisallowedModelAdminLookup("Filtering by %s not allowed" % key) filter_specs = [] if self.list_filter: for list_filter in self.list_filter: if callable(list_filter): # This is simply a custom list filter class. spec = list_filter(request, lookup_params, self.model, self.model_admin) else: field_path = None if isinstance(list_filter, (tuple, list)): # This is a custom FieldListFilter class for a given field. field, field_list_filter_class = list_filter else: # This is simply a field name, so use the default # FieldListFilter class that has been registered for # the type of the given field. field, field_list_filter_class = list_filter, FieldListFilter.create if not isinstance(field, models.Field): field_path = field field = get_fields_from_path(self.model, field_path)[-1] spec = field_list_filter_class(field, request, lookup_params, self.model, self.model_admin, field_path=field_path) # Check if we need to use distinct() use_distinct = (use_distinct or lookup_needs_distinct(self.lookup_opts, field_path)) if spec and spec.has_output(): filter_specs.append(spec) # At this point, all the parameters used by the various ListFilters # have been removed from lookup_params, which now only contains other # parameters passed via the query string. We now loop through the # remaining parameters both to ensure that all the parameters are valid # fields and to determine if at least one of them needs distinct(). If # the lookup parameters aren't real fields, then bail out. try: for key, value in lookup_params.items(): lookup_params[key] = prepare_lookup_value(key, value) use_distinct = (use_distinct or lookup_needs_distinct(self.lookup_opts, key)) return filter_specs, bool(filter_specs), lookup_params, use_distinct except FieldDoesNotExist as e: six.reraise(IncorrectLookupParameters, IncorrectLookupParameters(e), sys.exc_info()[2])
def get_filters(self, request): lookup_params = self.get_filters_params() use_distinct = False for key, value in lookup_params.items(): if not self.model_admin.lookup_allowed(key, value): raise DisallowedModelAdminLookup("Filtering by %s not allowed" % key) filter_specs = [] if self.list_filter: for list_filter in self.list_filter: if callable(list_filter): # This is simply a custom list filter class. spec = list_filter(request, lookup_params, self.model, self.model_admin) else: field_path = None if isinstance(list_filter, (tuple, list)): # This is a custom FieldListFilter class for a given field. field, field_list_filter_class = list_filter else: # This is simply a field name, so use the default # FieldListFilter class that has been registered for # the type of the given field. field, field_list_filter_class = list_filter, FieldListFilter.create if not isinstance(field, models.Field): field_path = field field = get_fields_from_path(self.model, field_path)[-1] lookup_params_count = len(lookup_params) spec = field_list_filter_class( field, request, lookup_params, self.model, self.model_admin, field_path=field_path ) # field_list_filter_class removes any lookup_params it # processes. If that happened, check if distinct() is # needed to remove duplicate results. if lookup_params_count > len(lookup_params): use_distinct = use_distinct or lookup_needs_distinct(self.lookup_opts, field_path) if spec and spec.has_output(): filter_specs.append(spec) # At this point, all the parameters used by the various ListFilters # have been removed from lookup_params, which now only contains other # parameters passed via the query string. We now loop through the # remaining parameters both to ensure that all the parameters are valid # fields and to determine if at least one of them needs distinct(). If # the lookup parameters aren't real fields, then bail out. try: for key, value in lookup_params.items(): lookup_params[key] = prepare_lookup_value(key, value) use_distinct = use_distinct or lookup_needs_distinct(self.lookup_opts, key) return filter_specs, bool(filter_specs), lookup_params, use_distinct except FieldDoesNotExist as e: raise IncorrectLookupParameters(e) from e
def get_filters(self): params = dict(self.request.GET.items()) opts = self.model._meta use_distinct = False list_filters = self.get_list_filters() new_params = {} has_filters = False # Normalize the types of keys list_names = [f if isinstance(f, str) else f.parameter_name for f in list_filters] for key, value in params.items(): # ignore keys not in list_filters if key.startswith(tuple(list_names)): new_params[force_str(key)] = value has_filters = bool(new_params) filter_specs = [] for list_filter in list_filters: if callable(list_filter): # This is simply a custom list filter class. spec = list_filter(self.request, new_params, self.model, None) else: field_path = None if isinstance(list_filter, (tuple, list)): # Custom FieldListFilter class for a given field. field, field_list_filter_class = list_filter else: # Field name, so use the default registered FieldListFilter field, field_list_filter_class = list_filter, FieldListFilter.create if not isinstance(field, models.Field): field_path = field field = get_fields_from_path(self.model, field_path)[-1] model_admin = admin.ModelAdmin(self.model, admin.site) spec = field_list_filter_class(field, self.request, new_params, self.model, model_admin, field_path=field_path) # Check if we need to use distinct() use_distinct = (use_distinct or lookup_needs_distinct(opts, field_path)) if spec and spec.has_output(): filter_specs.append(spec) # All the parameters used by the various ListFilters have been removed # lookup_params, now only contains other parameters passed via the query string. # We now loop through the remaining parameters both to ensure that all the parameters are valid # fields and to determine if at least one of them needs distinct(). If # the lookup parameters aren't real fields, then bail out. try: for key, value in new_params.items(): new_params[key] = prepare_lookup_value(key, value) use_distinct = (use_distinct or lookup_needs_distinct(opts, key)) return filter_specs, has_filters, use_distinct except FieldDoesNotExist as e: raise IncorrectLookupParameters from e
def filter_queryset(self, request, term, queryset=None, **dependent_fields): """ Return QuerySet filtered by search_fields matching the passed term. Args: request (django.http.request.HttpRequest): The request is being passed from the JSON view and can be used to dynamically alter the response queryset. term (str): Search term queryset (django.db.models.query.QuerySet): QuerySet to select choices from. **dependent_fields: Dependent fields and their values. If you want to inherit from ModelSelect2Mixin and later call to this method, be sure to pop everything from keyword arguments that is not a dependent field. Returns: QuerySet: Filtered QuerySet """ if queryset is None: queryset = self.get_queryset() search_fields = self.get_search_fields() select = Q() use_distinct = False if search_fields and term: for bit in term.split(): or_queries = [ Q(**{orm_lookup: bit}) for orm_lookup in search_fields ] select &= reduce(operator.or_, or_queries) or_queries = [ Q(**{orm_lookup: term}) for orm_lookup in search_fields ] select |= reduce(operator.or_, or_queries) use_distinct |= any( lookup_needs_distinct(queryset.model._meta, search_spec) for search_spec in search_fields) if dependent_fields: select &= Q(**dependent_fields) use_distinct |= any( lookup_needs_distinct(queryset.model._meta, search_spec) for search_spec in dependent_fields.keys()) if use_distinct: return queryset.filter(select).distinct() return queryset.filter(select)
def get_search_results(self, queryset, search_term): """Filter the results based on the query.""" search_fields = self.get_search_fields() if search_fields and search_term: orm_lookups = [ self._construct_search(search_field) for search_field in search_fields ] if self.split_words is not None: word_conditions = [] for word in search_term.split(): or_queries = [Q(**{orm_lookup: word}) for orm_lookup in orm_lookups] word_conditions.append(reduce(operator.or_, or_queries)) op_ = operator.or_ if self.split_words == "or" else operator.and_ queryset = queryset.filter(reduce(op_, word_conditions)) else: or_queries = [ Q(**{orm_lookup: search_term}) for orm_lookup in orm_lookups ] queryset = queryset.filter(reduce(operator.or_, or_queries)) if any( lookup_needs_distinct(queryset.model._meta, search_spec) for search_spec in orm_lookups ): queryset = queryset.distinct() return queryset
def _search(self, queryset): # Originally from django.contrib.admin.options 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.search_fields search_term = self.data.get(SEARCH_FIELD, '') if self.data else '' opts = self.model._meta 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 = [Q(**{orm_lookup: bit}) for orm_lookup in orm_lookups] queryset = queryset.filter(reduce(operator.or_, or_queries)) if not use_distinct: for search_spec in orm_lookups: if lookup_needs_distinct(opts, search_spec): use_distinct = True break return queryset, use_distinct
def get_search_results(self, request, queryset, search_term): def construct_search(field_name): if field_name.startswith('^'): return "%s__istartswith" % field_name[1:] elif field_name.startswith('@'): return "%s__search" % field_name[1:] else: return "%s__iexact" % field_name use_distinct = False search_fields = self.get_search_fields(request) 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 = [Q(**{orm_lookup: bit}) for orm_lookup in orm_lookups] queryset = queryset.filter(reduce(operator.or_, or_queries)) if not use_distinct: for search_spec in orm_lookups: if lookup_needs_distinct(self.opts, search_spec): use_distinct = True break return queryset, use_distinct
def get_search_results(self, request, queryset, search_term): """ Adapted from the defaulf implementation in django.contrib.admin.options, this get_search_results does not split the search term into tokens. """ # 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) if search_fields and search_term: orm_lookups = [construct_search(str(search_field)) for search_field in search_fields] or_queries = [models.Q(**{orm_lookup: search_term}) for orm_lookup in orm_lookups] queryset = queryset.filter(reduce(operator.or_, or_queries)) if not use_distinct: for search_spec in orm_lookups: if lookup_needs_distinct(self.opts, search_spec): use_distinct = True break return queryset, use_distinct
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 if self.search_fields and search_term: orm_lookups = [construct_search(str(search_field)) for search_field in self.search_fields] for bit in search_term.split(): or_queries = [models.Q(**{orm_lookup: bit}) for orm_lookup in orm_lookups] queryset = queryset.filter(reduce(operator.or_, or_queries)) if not use_distinct: for search_spec in orm_lookups: if lookup_needs_distinct(self.opts, search_spec): use_distinct = True break return queryset, use_distinct
def get_search_results(self, queryset, search_term): """ Generate a queryset according to the search term. Words in the search term are searched using the OR operator :param queryset: queryset to filter :param search_term: search string :return: tuple (queryset, distinct), distinct will be True if queryset is likely to contain duplicates """ def construct_search(field_name): if field_name.startswith('^'): return "{}__istartswith".format(field_name[1:]) elif field_name.startswith('='): return "{}__iexact".format(field_name[1:]) elif field_name.startswith('@'): return "{}__search".format(field_name[1:]) else: return "{}__icontains".format(field_name) opts = queryset.model._meta search_fields = self.get_list_search() use_distinct = False if search_fields and search_term: orm_lookups = [construct_search(str(search_field)) for search_field in search_fields] queryset = queryset.filter( reduce(operator.or_, [ models.Q(**{orm_lookup: bit}) for bit in search_term.split() for orm_lookup in orm_lookups ]) ) use_distinct = any(lookup_needs_distinct(opts, search_spec) for search_spec in orm_lookups) return queryset, use_distinct
def get_search_results(self, request, queryset, search_term): """ Copy-paste of https://github.com/django/django/blob/1.8.4/django/contrib/admin/options.py#L965-L996 Replace models.Q with mongoengine's Q 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) 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 = [Q(**{orm_lookup: bit}) for orm_lookup in orm_lookups] queryset = queryset.filter(reduce(operator.or_, or_queries)) if not use_distinct: for search_spec in orm_lookups: if lookup_needs_distinct(self.opts, search_spec): use_distinct = True break return queryset, use_distinct
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) 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 = [Q(**{orm_lookup: bit}) for orm_lookup in orm_lookups] queryset = queryset.filter(reduce(operator.or_, or_queries)) if not use_distinct: for search_spec in orm_lookups: if lookup_needs_distinct(self.opts, search_spec): use_distinct = True break return queryset, use_distinct
def queryset(self, request, queryset): from_option = self._values[0] if from_option and from_option != "" and from_option != "''": lookup = '%s__gte' % self.field_path if lookup_needs_distinct(self.model._meta, lookup): queryset = queryset.distinct() queryset = queryset.filter(**{lookup: from_option}) to_option = self._values[1] if to_option and to_option != "" and to_option != "''": lookup = '%s__lte' % self.field_path if lookup_needs_distinct(self.model._meta, lookup): queryset = queryset.distinct() queryset = queryset.filter(**{lookup: to_option}) return queryset
def queryset(self, request, queryset): search_option = self._values[0] if search_option and search_option != []: lookup = '%s%s__in' % (self.field_path, '_id') if lookup_needs_distinct(self.model._meta, lookup): queryset = queryset.distinct() queryset = queryset.filter(**{lookup: search_option}) return queryset
def annotation_friendly_search_results(self, request, queryset, search_term): # noqa """ - fully copied from django.... used nested select insteed of classic one - should be used if search is made by FK for example: userprofile__telephone... - why? because if not, django joins search tables and annotation result is multiplicated :( - see below """ # 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:] # 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 use_distinct = False search_fields = self.get_search_fields(request) 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 IS CHANGED!!!! # queryset = queryset.filter(reduce(operator.or_, or_queries)) filtered_ids = self.model.objects.filter( reduce(operator.or_, or_queries) ).values_list("id", flat=True) queryset = queryset.filter(id__in=filtered_ids) # ^^^^^^^ use_distinct |= any( lookup_needs_distinct(self.opts, search_spec) for search_spec in orm_lookups ) return queryset, use_distinct
def get_search_results(self, request, queryset, search_fields, search_term): """ Return a tuple containing a queryset to implement the search and a boolean indicating if the results may contain duplicates. """ # override ModelAdmin.get_search_results to use a custom search_fields # 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:] # 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 use_distinct = False 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 ] queryset = queryset.filter(reduce(operator.or_, or_queries)) use_distinct |= any( lookup_needs_distinct(queryset.model._meta, search_spec) for search_spec in orm_lookups) return queryset, use_distinct
def queryset(self, request, queryset): search_option = self._values[0] if search_option and search_option != '': lookup = '%s__exact' % self.field_path if lookup_needs_distinct(self.model._meta, lookup): queryset = queryset.distinct() if search_option == "True": queryset = queryset.filter(**{lookup: True}) if search_option == "False": queryset = queryset.filter(**{lookup: False}) return queryset
def queryset(self, request, queryset): search_terms = self._values[0] if search_terms and search_terms != '': lookup = '%s__icontains' % self.field_path if lookup_needs_distinct(self.model._meta, lookup): queryset = queryset.distinct() for bit in search_terms.split(): if bit.startswith('!'): queryset = queryset.exclude(**{lookup: bit[1:]}) else: queryset = queryset.filter(**{lookup: bit}) return queryset
def queryset(self, request, queryset): from_option = self._values[0] if from_option: if isinstance(from_option, text_type): from_option = parse_date(from_option) if from_option: lookup = '%s__gte' % self.field_path if lookup_needs_distinct(self.model._meta, lookup): queryset = queryset.distinct() queryset = queryset.filter(**{lookup: from_option}) to_option = self._values[1] if to_option: if isinstance(to_option, text_type): to_option = parse_date(to_option) if to_option: lookup = '%s__lte' % self.field_path if lookup_needs_distinct(self.model._meta, lookup): queryset = queryset.distinct() queryset = queryset.filter(**{lookup: to_option}) return queryset
def get_search_results(self, request, queryset, search_term): # 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:] # 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 query_field_params = request.GET.get('f', None) use_distinct = False search_fields = self.get_search_fields(request) if search_fields and search_term and query_field_params in search_fields: # orm_lookups = [construct_search(str(search_field)) # for search_field in search_fields] orm_lookups = [construct_search(str(query_field_params))] for bit in search_term.split(): or_queries = [ models.Q(**{orm_lookup: bit}) for orm_lookup in orm_lookups ] queryset = queryset.filter(reduce(operator.or_, or_queries)) use_distinct |= any( lookup_needs_distinct(self.opts, search_spec) for search_spec in orm_lookups) return queryset, use_distinct
def search_queryset(self, queryset, search_term, **kwargs): if not search_term or not self.search_fields: return queryset orm_lookups = ['%s__icontains' % str(search_field) for search_field in self.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)) opts = queryset.model._meta for search_spec in orm_lookups: if lookup_needs_distinct(opts, search_spec): return queryset.distinct() return queryset
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 # Group using quotes def unescape_string_literal_if_possible(bit): try: return unescape_string_literal(bit) except ValueError: return bit use_distinct = False search_fields = self.get_search_fields(request) if search_fields and search_term: search_term_list = [ unescape_string_literal_if_possible(bit) for bit in smart_split(search_term) ] orm_lookups = [ construct_search(str(search_field)) for search_field in search_fields ] for bit in search_term_list: or_queries = [ models.Q(**{orm_lookup: bit}) for orm_lookup in orm_lookups ] queryset = queryset.filter(reduce(operator.or_, or_queries)) if not use_distinct: for search_spec in orm_lookups: if lookup_needs_distinct(self.opts, search_spec): use_distinct = True break return queryset, use_distinct
def buscador(request, queryset, search_term='arroz'): return HttpResponse(request.GET['selec-model']) model = request.GET['selec-model']() search = request.GET['q'] queryset = model.objects.all() """ 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 = 'nombre' #get_search_fields(request) if search_fields and search_term: #orm_lookups = [construct_search(str(search_field)) # for search_field in search_fields] orm_lookups = [construct_search(str(search_fields))] for bit in search_term.split(): or_queries = [ models.Q(**{orm_lookup: bit}) for orm_lookup in orm_lookups ] queryset = queryset.filter(reduce(operator.or_, or_queries)) if not use_distinct: for search_spec in orm_lookups: if lookup_needs_distinct(opts, search_spec): use_distinct = True break for i in queryset: print i.nombre.upper() print i.nombre_cientifico.upper() return HttpResponse(queryset, use_distinct)
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. """ use_distinct = False if self.search_fields and search_term: orm_lookups = ['%s__icontains' % str(search_field) for search_field in self.search_fields] for bit in search_term.split(): or_queries = [models.Q(**{orm_lookup: bit}) for orm_lookup in orm_lookups] queryset = queryset.filter(reduce(operator.or_, or_queries)) if not use_distinct: for search_spec in orm_lookups: if lookup_needs_distinct(self.opts, search_spec): use_distinct = True break return queryset, use_distinct
def get_search_results(self, request, queryset, search_term): # 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__istartswith" % field_name # 优化级别1 # 优化级别2 暂时用不到 使用__istartswith # 优化级别3 暂时用不到 使用__iexact 会产生一个id相关的bug, 可调 use_distinct = False search_fields = self.get_search_fields(request) search_term = search_term.strip() if search_fields and search_term: orm_lookups = [ construct_search(str(search_field)) for search_field in search_fields ] # 优化or语句为多条sql, 获取每个条件的id list ids = [] for orm_lookup in orm_lookups: bit_ids = queryset.filter(**{ orm_lookup: search_term }).values_list('id', flat=True) ids.extend(bit_ids) queryset = queryset.filter(id__in=ids) if not use_distinct: for search_spec in orm_lookups: if lookup_needs_distinct(self.opts, search_spec): use_distinct = True break return queryset, use_distinct
def get_search_results(self, request, queryset, search_term): """ Copy-paste of https://github.com/django/django/blob/1.8.4/django/contrib/admin/options.py#L965-L996 Replace models.Q with mongoengine's Q 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) 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 = [ Q(**{orm_lookup: bit}) for orm_lookup in orm_lookups ] queryset = queryset.filter(reduce(operator.or_, or_queries)) if not use_distinct: for search_spec in orm_lookups: if lookup_needs_distinct(self.opts, search_spec): use_distinct = True break return queryset, use_distinct
def get_search_results(self, request, queryset, search_term): """ Adapted from the defaulf implementation in django.contrib.admin.options, this get_search_results does not split the search term into tokens. """ # 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) if search_fields and search_term: orm_lookups = [ construct_search(str(search_field)) for search_field in search_fields ] or_queries = [ models.Q(**{orm_lookup: search_term}) for orm_lookup in orm_lookups ] queryset = queryset.filter(reduce(operator.or_, or_queries)) if not use_distinct: for search_spec in orm_lookups: if lookup_needs_distinct(self.opts, search_spec): use_distinct = True break return queryset, use_distinct
def get_list_queryset(self, queryset): lookup_params = dict([ (smart_str(k)[len(FILTER_PREFIX):], v) for k, v in self.admin_view.params.items() if smart_str(k).startswith(FILTER_PREFIX) and v != '' ]) for p_key, p_val in iteritems(lookup_params): if p_val == "False": lookup_params[p_key] = False use_distinct = False # for clean filters self.admin_view.has_query_param = bool(lookup_params) self.admin_view.clean_query_url = self.admin_view.get_query_string( remove=[ k for k in self.request.GET.keys() if k.startswith(FILTER_PREFIX) ]) # Normalize the types of keys if not self.free_query_filter: for key, value in lookup_params.items(): if not self.lookup_allowed(key, value): raise SuspiciousOperation("Filtering by %s not allowed" % key) self.filter_specs = [] if self.list_filter: for list_filter in self.list_filter: if callable(list_filter): # This is simply a custom list filter class. spec = list_filter(self.request, lookup_params, self.model, self) else: field_path = None field_parts = [] if isinstance(list_filter, (tuple, list)): # This is a custom FieldListFilter class for a given field. field, field_list_filter_class = list_filter else: # This is simply a field name, so use the default # FieldListFilter class that has been registered for # the type of the given field. field, field_list_filter_class = list_filter, filter_manager.create if not isinstance(field, models.Field): field_path = field field_parts = get_fields_from_path( self.model, field_path) field = field_parts[-1] spec = field_list_filter_class(field, self.request, lookup_params, self.model, self.admin_view, field_path=field_path) if len(field_parts) > 1: # Add related model name to title spec.title = "%s %s" % (field_parts[-2].name, spec.title) # Check if we need to use distinct() use_distinct = (use_distinct or lookup_needs_distinct( self.opts, field_path)) if spec and spec.has_output(): try: new_qs = spec.do_filte(queryset) except ValidationError as e: new_qs = None self.admin_view.message_user( _("<b>Filtering error:</b> %s") % e.messages[0], 'error') if new_qs is not None: queryset = new_qs self.filter_specs.append(spec) self.has_filters = bool(self.filter_specs) self.admin_view.filter_specs = self.filter_specs obj = filter(lambda f: f.is_used, self.filter_specs) if six.PY3: obj = list(obj) self.admin_view.used_filter_num = len(obj) try: for key, value in lookup_params.items(): use_distinct = (use_distinct or lookup_needs_distinct(self.opts, key)) except FieldDoesNotExist as e: raise IncorrectLookupParameters(e) try: # fix a bug by david: In demo, quick filter by IDC Name() cannot be used. if isinstance(queryset, models.query.QuerySet) and lookup_params: new_lookup_parames = dict() for k, v in lookup_params.iteritems(): list_v = v.split(',') if len(list_v) > 0: new_lookup_parames.update({k: list_v}) else: new_lookup_parames.update({k: v}) queryset = queryset.filter(**new_lookup_parames) except (SuspiciousOperation, ImproperlyConfigured): raise except Exception as e: raise IncorrectLookupParameters(e) else: if not isinstance(queryset, models.query.QuerySet): pass query = self.request.GET.get(SEARCH_VAR, '') # 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 query: orm_lookups = [ construct_search(str(search_field)) for search_field in self.search_fields ] for bit in query.split(): or_queries = [ models.Q(**{orm_lookup: bit}) for orm_lookup in orm_lookups ] queryset = queryset.filter(reduce(operator.or_, or_queries)) if not use_distinct: for search_spec in orm_lookups: if lookup_needs_distinct(self.opts, search_spec): use_distinct = True break self.admin_view.search_query = query if use_distinct: return queryset.distinct() else: return queryset
def get_filters(self, request): lookup_params = self.get_filters_params() use_distinct = False for key, value in lookup_params.items(): if not self.model_admin.lookup_allowed(key, value): raise DisallowedModelAdminLookup( "Filtering by %s not allowed" % key) filter_specs = [] for list_filter in self.list_filter: if callable(list_filter): # This is simply a custom list filter class. spec = list_filter(request, lookup_params, self.model, self.model_admin) else: field_path = None if isinstance(list_filter, (tuple, list)): # This is a custom FieldListFilter class for a given field. field, field_list_filter_class = list_filter else: # This is simply a field name, so use the default # FieldListFilter class that has been registered for the # type of the given field. field, field_list_filter_class = list_filter, FieldListFilter.create if not isinstance(field, models.Field): field_path = field field = get_fields_from_path(self.model, field_path)[-1] lookup_params_count = len(lookup_params) spec = field_list_filter_class( field, request, lookup_params, self.model, self.model_admin, field_path=field_path, ) # field_list_filter_class removes any lookup_params it # processes. If that happened, check if distinct() is needed to # remove duplicate results. if lookup_params_count > len(lookup_params): use_distinct = use_distinct or lookup_needs_distinct( self.lookup_opts, field_path) if spec and spec.has_output(): filter_specs.append(spec) if self.date_hierarchy: # Create bounded lookup parameters so that the query is more # efficient. year = lookup_params.pop('%s__year' % self.date_hierarchy, None) if year is not None: month = lookup_params.pop('%s__month' % self.date_hierarchy, None) day = lookup_params.pop('%s__day' % self.date_hierarchy, None) try: from_date = datetime( int(year), int(month if month is not None else 1), int(day if day is not None else 1), ) except ValueError as e: raise IncorrectLookupParameters(e) from e if settings.USE_TZ: from_date = make_aware(from_date) if day: to_date = from_date + timedelta(days=1) elif month: # In this branch, from_date will always be the first of a # month, so advancing 32 days gives the next month. to_date = (from_date + timedelta(days=32)).replace(day=1) else: to_date = from_date.replace(year=from_date.year + 1) lookup_params.update({ '%s__gte' % self.date_hierarchy: from_date, '%s__lt' % self.date_hierarchy: to_date, }) # At this point, all the parameters used by the various ListFilters # have been removed from lookup_params, which now only contains other # parameters passed via the query string. We now loop through the # remaining parameters both to ensure that all the parameters are valid # fields and to determine if at least one of them needs distinct(). If # the lookup parameters aren't real fields, then bail out. try: for key, value in lookup_params.items(): lookup_params[key] = prepare_lookup_value(key, value) use_distinct = use_distinct or lookup_needs_distinct( self.lookup_opts, key) return filter_specs, bool( filter_specs), lookup_params, use_distinct except FieldDoesNotExist as e: raise IncorrectLookupParameters(e) from e
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. """ # BEGIN CUSTOMIZATION # search_terms = self.get_search_terms(search_term) # END CUSTOMIZATION # # 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) # BEGIN CUSTOMIZATION # if search_terms: for search_term in search_terms: qs = self.model.objects.none() # Create an empty queryset # END CUSTOMIZATION # 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 ] # BEGIN CUSTOMIZATION # qs |= self.get_method_for_queryset(queryset)( reduce(operator.or_, or_queries) ) # END CUSTOMIZATION # if not use_distinct: for search_spec in orm_lookups: if lookup_needs_distinct(self.opts, search_spec): use_distinct = True break # BEGIN CUSTOMIZATION # else: qs = queryset if hasattr(self, "filter_class") and hasattr(self, "filterset_params"): kwargs = {"request": request, "queryset": qs, "passed_validation": True} if issubclass(self.filter_class, SemanticExcludeAllFilterSet): kwargs["exclude"] = self.filterset_exclude filterset = self.filter_class(self.filterset_params, **kwargs) try: filterset = self.filter_class(self.filterset_params, **kwargs) except Exception: pass else: self.filterset = filterset qs = filterset.qs # END CUSTOMIZATION # return qs, use_distinct
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 query: orm_lookups = [construct_search(str(search_field)) for search_field in self.search_fields] for bit in query.split(): or_queries = [models.Q(**{orm_lookup: bit}) for orm_lookup in orm_lookups] queryset = queryset.filter(reduce(operator.or_, or_queries)) if not use_distinct: for search_spec in orm_lookups: if lookup_needs_distinct(self.opts, search_spec): use_distinct = True break self.admin_view.search_query = query if use_distinct: return queryset.distinct() else: return queryset # Media def get_media(self, media): if bool(filter(lambda s: isinstance(s, DateFieldListFilter), self.filter_specs)): media = media + self.vendor('datepicker.css', 'datepicker.js', 'xadmin.widget.datetime.js') if bool(filter(lambda s: isinstance(s, RelatedFieldSearchFilter), self.filter_specs)):
def get_search_results(self, request, queryset, search_term): def generate_q_object(orm_lookups): """ Generate Or'ed queries from orm_lookups (fields) and every bit of the search_term (query). """ q = Q() for bit in search_term.split(): or_queries = [ Q(**{orm_lookup: bit}) for orm_lookup in orm_lookups ] if or_queries: q = (q & functools.reduce(operator.or_, or_queries)) return q # Apply keyword searches. def construct_search(field_name): """ Parse field_name to allow advanced searches using the prefixes: '^', '=', '@' and no prefix (default) """ 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 def parse_related_fields(): """ Go over the search_fields to look for fields that exist in the related_search_mapping """ normal_fields = [] generic_search_fields = defaultdict(list) for field in self.search_fields: for rfield in self.related_search_mapping: if field.startswith(rfield): inner_field = field[len(rfield) + 2:] generic_search_fields[rfield].append( # get the field name after 'rfield__' construct_search(inner_field)) else: normal_fields.append(field) return normal_fields, generic_search_fields def get_generic_field(model, field_name): """ Find the given generic_field name in the given model and verify it is a GenericForeignKey, otherwise raise an Exeption. """ for f in model._meta.virtual_fields: if f.name == field_name: if not isinstance(f, GenericForeignKey): raise Exception('Given field %s is not an instance of ' 'GenericForeignKey' % field_name) return f def get_object_id(model, generic_field): """ Return the foreign key field for a given GenericForeignKey in a given model """ self.logger.debug( 'related_search_mapping did not define object_id, ' 'attempting to find using GenericForeignKey %s in ' 'model %s', generic_field, model) field = get_generic_field(model, generic_field) if field: return field.fk_field raise Exception('Given field %s does not exist in registered model' ' %s and no object_id provided' % (generic_field, model)) def get_content_types(model, generic_field): """ Return the content types allowed for a given GenericForeignKey in a given model """ self.logger.debug( 'related_search_mapping did not define ctypes, ' 'attempting to find using GenericForeignKey %s in ' 'model %s', generic_field, model) field = get_generic_field(model, generic_field) if field: return field.ct_field raise Exception('Given field %s does not exist in registered model' ' %s and no object_id provided' % (generic_field, model)) def get_related_ids(fields_mapping): """ Takes a dict of {generic_field_name: list_of_inner_Fields}, performs the query on the related object models (using defined or calculated content types) and returns the ids of the result objects. """ def get_ctype_models(ctypes): """ Gets model classes from the passed argument, which can be: a. a dict which can be extrapolated into a query filter. b. a Q object which can be passed to a query filter. c. an iterable of 2 element tuples as (app_label, model) """ if isinstance(ctypes, dict): if not ctypes: self.logger.warn(""" This is a very inefficient query! Each search argument is going to query all model classes. Please limit ContentType choices the FK if possible, or define a 'related_search_mapping' argument which limits the ctypes.""") return [ ct.model_class() for ct in ContentType.objects.filter(**ctypes) ] elif isinstance(ctypes, Q): return [ ct.model_class() for ct in ContentType.objects.filter(ctypes) ] elif isinstance(ctypes, Iterable): if issubclass(ctypes[0], django.db.models.Model): return ctypes else: return [ ContentType.objects.get(app_label=app, model=model).model_class() for app, model in ctypes ] raise Exception("Invalid argument passed, must be one of: " "<dict>, <Q>, <iterable of 2 elem. tuples>") or_queries = [] for rel_field, fields in fields_mapping.items(): query = generate_q_object(fields) if not query: self.logger.warn('No Q instance returned') continue cont_type = ( self.related_search_mapping[rel_field].get('content_type')) obj_id = ( self.related_search_mapping[rel_field].get('object_id') or get_object_id(self.model, rel_field)) ctypes = (self.related_search_mapping[rel_field].get('ctypes') or get_content_types(self.model, rel_field)) models = get_ctype_models(ctypes) for model in models: result = model.objects.filter(query).values_list('pk', flat=True) or_queries.append( Q(**{'%s__in' % obj_id: result}) & Q( **{ '%s' % cont_type: ContentType.objects.get_for_model(model) })) return or_queries use_distinct = False if not search_term: return queryset, use_distinct non_generic_fields, generic_fields = parse_related_fields() related_query = get_related_ids(generic_fields) # initial orm lookups (for normal fields) orm_lookups = [ construct_search(str(search_field)) for search_field in non_generic_fields ] for bit in search_term.split(): or_queries = [Q(**{orm_lookup: bit}) for orm_lookup in orm_lookups] or_queries = or_queries + related_query query = functools.reduce(operator.or_, or_queries) queryset = queryset.filter(query) if not use_distinct: for search_spec in orm_lookups: if lookup_needs_distinct(self.opts, search_spec): use_distinct = True break return queryset, use_distinct
def get_list_queryset(self, queryset): lookup_params = dict([(smart_str(k)[len(FILTER_PREFIX):], v) for k, v in self.admin_view.params.items() if smart_str(k).startswith(FILTER_PREFIX) and v != '']) for p_key, p_val in lookup_params.iteritems(): if p_val == "False": lookup_params[p_key] = False use_distinct = False # for clean filters self.admin_view.has_query_param = bool(lookup_params) self.admin_view.clean_query_url = self.admin_view.get_query_string(remove= [k for k in self.request.GET.keys() if k.startswith(FILTER_PREFIX)]) # Normalize the types of keys if not self.free_query_filter: for key, value in lookup_params.items(): if not self.lookup_allowed(key, value): raise SuspiciousOperation( "Filtering by %s not allowed" % key) self.filter_specs = [] if self.list_filter: for list_filter in self.list_filter: if callable(list_filter): # This is simply a custom list filter class. spec = list_filter(self.request, lookup_params, self.model, self) else: field_path = None field_parts = [] if isinstance(list_filter, (tuple, list)): # This is a custom FieldListFilter class for a given field. field, field_list_filter_class = list_filter else: # This is simply a field name, so use the default # FieldListFilter class that has been registered for # the type of the given field. field, field_list_filter_class = list_filter, filter_manager.create if not isinstance(field, models.Field): field_path = field field_parts = get_fields_from_path( self.model, field_path) field = field_parts[-1] spec = field_list_filter_class( field, self.request, lookup_params, self.model, self.admin_view, field_path=field_path) if len(field_parts)>1: # Add related model name to title spec.title = "%s %s"%(field_parts[-2].name,spec.title) # Check if we need to use distinct() use_distinct = (use_distinct or lookup_needs_distinct(self.opts, field_path)) if spec and spec.has_output(): try: new_qs = spec.do_filte(queryset) except ValidationError, e: new_qs = None self.admin_view.message_user(_("<b>Filtering error:</b> %s") % e.messages[0], 'error') if new_qs is not None: queryset = new_qs self.filter_specs.append(spec)
def get_list_queryset(self, queryset): lookup_params = dict([(smart_str(k)[len(FILTER_PREFIX):], v) for k, v in self.admin_view.params.items() if smart_str(k).startswith(FILTER_PREFIX) and v != '']) for p_key, p_val in iteritems(lookup_params): if p_val == "False": lookup_params[p_key] = False use_distinct = False # for clean filters self.admin_view.has_query_param = bool(lookup_params) self.admin_view.clean_query_url = self.admin_view.get_query_string(remove=[k for k in self.request.GET.keys() if k.startswith(FILTER_PREFIX)]) # Normalize the types of keys if not self.free_query_filter: for key, value in lookup_params.items(): if not self.lookup_allowed(key, value): raise SuspiciousOperation( "Filtering by %s not allowed" % key) self.filter_specs = [] if self.list_filter: for list_filter in self.list_filter: if callable(list_filter): # This is simply a custom list filter class. spec = list_filter(self.request, lookup_params, self.model, self) else: field_path = None field_parts = [] if isinstance(list_filter, (tuple, list)): # This is a custom FieldListFilter class for a given field. field, field_list_filter_class = list_filter else: # This is simply a field name, so use the default # FieldListFilter class that has been registered for # the type of the given field. field, field_list_filter_class = list_filter, filter_manager.create if not isinstance(field, models.Field): field_path = field field_parts = get_fields_from_path( self.model, field_path) field = field_parts[-1] spec = field_list_filter_class( field, self.request, lookup_params, self.model, self.admin_view, field_path=field_path) if len(field_parts) > 1: # Add related model name to title spec.title = "%s %s" % (field_parts[-2].name, spec.title) # Check if we need to use distinct() use_distinct = (use_distinct or lookup_needs_distinct(self.opts, field_path)) if spec and spec.has_output(): try: new_qs = spec.do_filte(queryset) except ValidationError as e: new_qs = None self.admin_view.message_user(_("<b>Filtering error:</b> %s") % e.messages[0], 'error') if new_qs is not None: queryset = new_qs self.filter_specs.append(spec) self.has_filters = bool(self.filter_specs) self.admin_view.filter_specs = self.filter_specs obj = filter(lambda f: f.is_used, self.filter_specs) if six.PY3: obj = list(obj) self.admin_view.used_filter_num = len(obj) try: for key, value in lookup_params.items(): use_distinct = ( use_distinct or lookup_needs_distinct(self.opts, key)) except FieldDoesNotExist as e: raise IncorrectLookupParameters(e) try: # fix a bug by david: In demo, quick filter by IDC Name() cannot be used. if isinstance(queryset, models.query.QuerySet) and lookup_params: new_lookup_parames = dict() for k, v in lookup_params.items(): list_v = v.split(',') if len(list_v) > 0: new_lookup_parames.update({k: list_v}) else: new_lookup_parames.update({k: v}) queryset = queryset.filter(**new_lookup_parames) except (SuspiciousOperation, ImproperlyConfigured): raise except Exception as e: raise IncorrectLookupParameters(e) else: if not isinstance(queryset, models.query.QuerySet): pass query = self.request.GET.get(SEARCH_VAR, '') # 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 query: orm_lookups = [construct_search(str(search_field)) for search_field in self.search_fields] for bit in query.split(): or_queries = [models.Q(**{orm_lookup: bit}) for orm_lookup in orm_lookups] queryset = queryset.filter(reduce(operator.or_, or_queries)) if not use_distinct: for search_spec in orm_lookups: if lookup_needs_distinct(self.opts, search_spec): use_distinct = True break self.admin_view.search_query = query if use_distinct: return queryset.distinct() else: return queryset
class FilterPlugin(BaseAdminPlugin): list_filter = () search_fields = () free_query_filter = True def lookup_allowed(self, lookup, value): model = self.model # Check FKey lookups that are allowed, so that popups produced by # ForeignKeyRawIdWidget, on the basis of ForeignKey.limit_choices_to, # are allowed to work. for l in model._meta.related_fkey_lookups: for k, v in widgets.url_params_from_lookup_dict(l).items(): if k == lookup and v == value: return True parts = lookup.split(LOOKUP_SEP) # Last term in lookup is a query term (__exact, __startswith etc) # This term can be ignored. if len(parts) > 1 and parts[-1] in QUERY_TERMS: parts.pop() # Special case -- foo__id__exact and foo__id queries are implied # if foo has been specificially included in the lookup list; so # drop __id if it is the last part. However, first we need to find # the pk attribute name. rel_name = None for part in parts[:-1]: try: field = model._meta.get_field(part) except FieldDoesNotExist: # Lookups on non-existants fields are ok, since they're ignored # later. return True if hasattr(field, 'rel'): model = field.rel.to rel_name = field.rel.get_related_field().name elif is_related_field(field): model = field.model rel_name = model._meta.pk.name else: rel_name = None if rel_name and len(parts) > 1 and parts[-1] == rel_name: parts.pop() if len(parts) == 1: return True clean_lookup = LOOKUP_SEP.join(parts) return clean_lookup in self.list_filter def get_list_queryset(self, queryset): lookup_params = dict([(smart_str(k)[len(FILTER_PREFIX):], v) for k, v in self.admin_view.params.items() if smart_str(k).startswith(FILTER_PREFIX) and v != '']) for p_key, p_val in lookup_params.iteritems(): if p_val == "False": lookup_params[p_key] = False use_distinct = False # for clean filters self.admin_view.has_query_param = bool(lookup_params) self.admin_view.clean_query_url = self.admin_view.get_query_string(remove= [k for k in self.request.GET.keys() if k.startswith(FILTER_PREFIX)]) # Normalize the types of keys if not self.free_query_filter: for key, value in lookup_params.items(): if not self.lookup_allowed(key, value): raise SuspiciousOperation( "Filtering by %s not allowed" % key) self.filter_specs = [] if self.list_filter: for list_filter in self.list_filter: if callable(list_filter): # This is simply a custom list filter class. spec = list_filter(self.request, lookup_params, self.model, self) else: field_path = None field_parts = [] if isinstance(list_filter, (tuple, list)): # This is a custom FieldListFilter class for a given field. field, field_list_filter_class = list_filter else: # This is simply a field name, so use the default # FieldListFilter class that has been registered for # the type of the given field. field, field_list_filter_class = list_filter, filter_manager.create if not isinstance(field, models.Field): field_path = field field_parts = get_fields_from_path( self.model, field_path) field = field_parts[-1] spec = field_list_filter_class( field, self.request, lookup_params, self.model, self.admin_view, field_path=field_path) if len(field_parts)>1: # Add related model name to title spec.title = "%s %s"%(field_parts[-2].name,spec.title) # Check if we need to use distinct() use_distinct = (use_distinct or lookup_needs_distinct(self.opts, field_path)) if spec and spec.has_output(): try: new_qs = spec.do_filte(queryset) except ValidationError, e: new_qs = None self.admin_view.message_user(_("<b>Filtering error:</b> %s") % e.messages[0], 'error') if new_qs is not None: queryset = new_qs self.filter_specs.append(spec) self.has_filters = bool(self.filter_specs) self.admin_view.filter_specs = self.filter_specs self.admin_view.used_filter_num = len( filter(lambda f: f.is_used, self.filter_specs)) try: for key, value in lookup_params.items(): use_distinct = ( use_distinct or lookup_needs_distinct(self.opts, key)) except FieldDoesNotExist, e: raise IncorrectLookupParameters(e)
def get_filters(self, request): lookup_params = self.get_filters_params() use_distinct = False for key, value in lookup_params.items(): if not self.model_admin.lookup_allowed(key, value): raise DisallowedModelAdminLookup("Filtering by %s not allowed" % key) filter_specs = [] for list_filter in self.list_filter: if callable(list_filter): # This is simply a custom list filter class. spec = list_filter(request, lookup_params, self.model, self.model_admin) else: field_path = None if isinstance(list_filter, (tuple, list)): # This is a custom FieldListFilter class for a given field. field, field_list_filter_class = list_filter else: # This is simply a field name, so use the default # FieldListFilter class that has been registered for the # type of the given field. field, field_list_filter_class = list_filter, FieldListFilter.create if not isinstance(field, models.Field): field_path = field field = get_fields_from_path(self.model, field_path)[-1] lookup_params_count = len(lookup_params) spec = field_list_filter_class( field, request, lookup_params, self.model, self.model_admin, field_path=field_path, ) # field_list_filter_class removes any lookup_params it # processes. If that happened, check if distinct() is needed to # remove duplicate results. if lookup_params_count > len(lookup_params): use_distinct = use_distinct or lookup_needs_distinct(self.lookup_opts, field_path) if spec and spec.has_output(): filter_specs.append(spec) if self.date_hierarchy: # Create bounded lookup parameters so that the query is more # efficient. year = lookup_params.pop('%s__year' % self.date_hierarchy, None) if year is not None: month = lookup_params.pop('%s__month' % self.date_hierarchy, None) day = lookup_params.pop('%s__day' % self.date_hierarchy, None) try: from_date = datetime( int(year), int(month if month is not None else 1), int(day if day is not None else 1), ) except ValueError as e: raise IncorrectLookupParameters(e) from e if settings.USE_TZ: from_date = make_aware(from_date) if day: to_date = from_date + timedelta(days=1) elif month: # In this branch, from_date will always be the first of a # month, so advancing 32 days gives the next month. to_date = (from_date + timedelta(days=32)).replace(day=1) else: to_date = from_date.replace(year=from_date.year + 1) lookup_params.update({ '%s__gte' % self.date_hierarchy: from_date, '%s__lt' % self.date_hierarchy: to_date, }) # At this point, all the parameters used by the various ListFilters # have been removed from lookup_params, which now only contains other # parameters passed via the query string. We now loop through the # remaining parameters both to ensure that all the parameters are valid # fields and to determine if at least one of them needs distinct(). If # the lookup parameters aren't real fields, then bail out. try: for key, value in lookup_params.items(): lookup_params[key] = prepare_lookup_value(key, value) use_distinct = use_distinct or lookup_needs_distinct(self.lookup_opts, key) return filter_specs, bool(filter_specs), lookup_params, use_distinct except FieldDoesNotExist as e: raise IncorrectLookupParameters(e) from e