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 # 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, BaseField): 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_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. """ self.opts = Team._meta # 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 filter_index(self, active_index): use_distinct = False query = self.value() 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] active_index = active_index.filter(reduce(operator.or_, or_queries)) if not use_distinct: for search_spec in orm_lookups: if lookup_needs_distinct(self.resource.opts, search_spec): use_distinct = True break if use_distinct: return active_index.distinct() return active_index
def get_queryset(self, request): u'''Модифицирует self.qs. Поиск.''' query = request.GET.get(SEARCH_VAR, '') qs = self.qs 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] qs = qs.filter(reduce(operator.or_, or_queries)) for search_spec in orm_lookups: if lookup_needs_distinct(self.opts, search_spec): qs = qs.distinct() break return qs ########################################################################## ##########################################################################
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_indexes(self): #from hyperadmin.resources.indexes import Index from hyperadmin.resources.models.filters import FieldFilter, SearchFilter from django.db import models from django.contrib.admin.util import get_fields_from_path try: from django.contrib.admin.util import lookup_needs_distinct except ImportError: from hyperadmin.resources.models.util import lookup_needs_distinct indexes = {'primary': ModelIndex('primary', self)} index = ModelIndex('filter', self) indexes['filter'] = index if self.list_filter: for list_filter in self.list_filter: use_distinct = False if callable(list_filter): # This is simply a custom list filter class. spec = list_filter(index=index) 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, FieldFilter.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, field_path=field_path, index=index) # Check if we need to use distinct() use_distinct = (use_distinct or lookup_needs_distinct( self.opts, field_path)) if spec: index.filters.append(spec) if self.search_fields: index.register_filter(SearchFilter, search_fields=self.search_fields) ''' date_section = self.register_section('date', FilterSection) if self.date_hierarchy: pass ''' return indexes
def _search(self, queryset): if 'search' in self.cleaned_data: lookups = [_search_lookup(str(field)) for field in self.search] for bit in self.cleaned_data['search'].split(): qlist = [models.Q(**{lookup: bit}) for lookup in lookups] queryset = queryset.filter(reduce(operator.or_, qlist)) if not self.use_distinct: for lookup in lookups: if lookup_needs_distinct(self.model._meta, lookup): self.use_distinct = True break return queryset
def get_indexes(self): #from hyperadmin.resources.indexes import Index from hyperadmin.resources.models.filters import FieldFilter, SearchFilter from django.db import models from django.contrib.admin.util import get_fields_from_path try: from django.contrib.admin.util import lookup_needs_distinct except ImportError: from hyperadmin.resources.models.util import lookup_needs_distinct indexes = {'primary': ModelIndex('primary', self)} index = ModelIndex('filter', self) indexes['filter'] = index if self.list_filter: for list_filter in self.list_filter: use_distinct = False if callable(list_filter): # This is simply a custom list filter class. spec = list_filter(index=index) 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, FieldFilter.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, field_path=field_path, index=index) # Check if we need to use distinct() use_distinct = (use_distinct or lookup_needs_distinct(self.opts, field_path)) if spec: index.filters.append(spec) if self.search_fields: index.register_filter(SearchFilter, search_fields=self.search_fields) ''' date_section = self.register_section('date', FilterSection) if self.date_hierarchy: pass ''' return indexes
def get_filters(self): lookup_params = dict(self.request.GET.items()) use_distinct = False filter_specs = [] list_filters = self.list_filter if list_filters: for list_filter in list_filters: if callable(list_filter): # This is simply a custom list filter class. spec = list_filter( self.request, lookup_params, self.model_class, self, ) 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_class, field_path)[-1] spec = field_list_filter_class( field, self.request, lookup_params, self.model_class, self, field_path=field_path ) # Check if we need to use distinct() use_distinct = ( use_distinct or lookup_needs_distinct( self.model_class._meta, field_path ) ) if spec and spec.has_output(): filter_specs.append(spec) self.filter_specs = filter_specs return filter_specs, bool(filter_specs), use_distinct
def search(self, 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] self = self.filter(reduce(operator.or_, or_queries)) for search_spec in orm_lookups: if lookup_needs_distinct(self.model._meta, search_spec): self = self.distinct() break return self
def search(self, 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 ] self = self.filter(reduce(operator.or_, or_queries)) for search_spec in orm_lookups: if lookup_needs_distinct(self.model._meta, search_spec): self = self.distinct() break return self
def cell_filters(self): lookup_params = self.params.copy() # a dictionary of the query string cell_filter_specs = {} use_distinct = False if self.model_admin.cell_filter: for cell_filter in self.model_admin.cell_filter: path = field_path = None field, field_list_filter_class = cell_filter, FieldCellFilter if hasattr(self.model_admin, cell_filter): # if it's a ModelAdmin method get the `admin_filter_field` attr = getattr(self.model_admin, cell_filter) field_path = getattr(attr, 'admin_filter_field', None) if not field_path: continue path = get_fields_from_path(self.model, field_path) field = path[-1] if not isinstance(field, models.Field): try: field_path = field field = get_fields_from_path(self.model, field_path)[-1] except FieldDoesNotExist: raise Exception( "Cannot use field `%s` in cell_filter. Only valid Field objects are allowed" % cell_filter) if isinstance(field, BooleanField): field_list_filter_class = BooleanCellFilter elif hasattr(field, 'rel') and bool(field.rel): field_list_filter_class = RelatedFieldCellFilter elif hasattr(field, 'choices'): field_list_filter_class = ChoicesCellFilter spec = field_list_filter_class(field, self.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(): cell_filter_specs[cell_filter] = spec return cell_filter_specs
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] for bit in self.query.split(): or_queries = [models.Q(**{orm_lookup: bit}) for orm_lookup in orm_lookups] 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 def url_for_result(self, result): return "%s/" % quote(getattr(result, self.pk_attname))
def get_query_set(self, request): '''qs = self.root_query_set if not(request.GET.has_key('q')): return EmptyQuerySet() (self.filter_specs, self.has_filters, remaining_lookup_params, use_distinct) = self.get_filters(request) for filter_spec in self.filter_specs: new_qs = filter_spec.queryset(request, qs) if new_qs is not None: qs = new_qs try: qs = qs.filter(**remaining_lookup_params) except (SuspiciousOperation, ImproperlyConfigured): raise except Exception as e: raise IncorrectLookupParameters(e) order_queryset = qs if self.search_fields and self.query: orm_lookups = ["%s__icontains" % (str(search_field)) for search_field in self.search_fields] for bit in self.query.split(): or_queries = [models.Q(**{orm_lookup: bit})for orm_lookup in orm_lookups] qs = qs.filter(reduce(operator.or_, or_queries)) queryset = set(qs.values_list('id', flat=True)) search_world = (request.GET['q']).lower().strip() profile_objects = Profile.objects.select_related().exclude(user__id__in=qs) for pr in profile_objects: if compare(pr.get_company_name_or_family().lower(), search_world): queryset.add(pr.user.id) qs = order_queryset.filter(pk__in=queryset) if not use_distinct: for search_spec in orm_lookups: if lookup_needs_distinct(self.lookup_opts, search_spec): use_distinct = True break''' #--------startMy-------------------- qs = self.root_query_set if not(request.GET.has_key('q')): return EmptyQuerySet() (self.filter_specs, self.has_filters, remaining_lookup_params, use_distinct) = self.get_filters(request) for filter_spec in self.filter_specs: new_qs = filter_spec.queryset(request, qs) if new_qs is not None: qs = new_qs try: qs = qs.filter(**remaining_lookup_params) except (SuspiciousOperation, ImproperlyConfigured): raise except Exception as e: raise IncorrectLookupParameters(e) if self.search_fields and self.query: orm_lookups = ["user__%s__icontains" % (str(search_field)) for search_field in self.search_fields] search_fields2 = ["first_name", "last_name", "company_name", "legal_form", "billing_account_id"] orm_lookups3 = ["%s__icontains" % (str(search_field)) for search_field in self.search_fields] orm_lookups2 = ["%s__icontains" % (str(search_field)) for search_field in search_fields2] orm_lookups += orm_lookups2 print orm_lookups profls = Profile.objects.select_related("user").all() for bit in self.query.split(): or_queries = [models.Q(**{orm_lookup: bit})for orm_lookup in orm_lookups] profls = profls.filter(reduce(operator.or_, or_queries)) ids = [profl.user_id for profl in profls] qs = qs.filter(pk__in=ids) if not use_distinct: for search_spec in orm_lookups3: if lookup_needs_distinct(self.lookup_opts, search_spec): use_distinct = True break #--------endMy-------------------- ordering = self.get_ordering(request, qs) qs = qs.order_by(*ordering) if use_distinct: return qs.distinct() else: return qs
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 & 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_object_id(model, generic_field): """ Return the foreign key field for a given GenericForeignKey in a given model """ 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 """ 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: 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): 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>") ids = defaultdict(list) for rel_field, fields in list(fields_mapping.items()): query = generate_q_object(fields) if not query: logger.warn('No Q instance returned') continue 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: ids[obj_id].extend( model.objects.filter(query).values_list('pk', flat=True)) return ids use_distinct = False if not search_term: return queryset, use_distinct non_generic_fields, generic_fields = parse_related_fields() related_ids = 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] # append generic related filters to or_queries for obj_id, ids_list in list(related_ids.items()): or_queries.append(Q(**{'%s__in' % obj_id: ids_list})) query = 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_queryset(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_queryset 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 as 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 ] for bit in self.query.split(): or_queries = [ models.Q(**{orm_lookup: bit}) for orm_lookup in orm_lookups ] 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
def get_filters(self, request): if not request.session.get('use_new_filters'): return super(CustomChangeList, self).get_filters(request) new_filter, created = CustomFilter.objects.get_or_create( user=request.user, model_name=self.model.__name__, app_name=self.model._meta.app_label, default=True) form = CustomFilterForm(request.GET.copy(), custom_filter=new_filter) if len(request.GET) and form.is_valid(): form.save() self.current_filter = CustomFilter.objects.filter( user=request.user, path_info=request.path_info, default=True) # loading filter set params into change list, so they will be applied in queryset if self.current_filter: filter_params, self.exclude_params, self.bundled_params = self.current_filter[ 0].get_filter_params() self.params.update(**filter_params) lookup_params = self.params.copy() # a dictionary of the query string use_distinct = False # Remove all the parameters that are globally and systematically # ignored. for ignored in IGNORED_PARAMS: if ignored in lookup_params: del lookup_params[ignored] # 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[smart_str(key)] = value if not self.model_admin.lookup_allowed(key, value): raise SuspiciousOperation("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. for key, value in lookup_params.items(): lookup_params[key] = prepare_lookup_value(key, value) try: use_distinct = (use_distinct or lookup_needs_distinct( self.lookup_opts, key)) except FieldDoesNotExist, e: lookup_params.pop(key)
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 as 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] for bit in self.query.split(): or_queries = [models.Q(**{orm_lookup: bit}) for orm_lookup in orm_lookups] 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
class ChangeList(object): def __init__(self, request, model, list_display, list_display_links, list_filter, date_hierarchy, search_fields, list_select_related, list_per_page, list_max_show_all, list_editable, model_admin): self.model = model self.opts = model._meta self.lookup_opts = self.opts self.root_query_set = model_admin.queryset(request) self.list_display = list_display self.list_display_links = list_display_links self.list_filter = list_filter self.date_hierarchy = date_hierarchy self.search_fields = search_fields self.list_select_related = list_select_related self.list_per_page = list_per_page self.list_max_show_all = list_max_show_all self.model_admin = model_admin # Get search parameters from the query string. try: self.page_num = int(request.GET.get(PAGE_VAR, 0)) except ValueError: self.page_num = 0 self.show_all = ALL_VAR in request.GET self.is_popup = IS_POPUP_VAR in request.GET self.to_field = request.GET.get(TO_FIELD_VAR) self.params = dict(request.GET.items()) if PAGE_VAR in self.params: del self.params[PAGE_VAR] if ERROR_FLAG in self.params: del self.params[ERROR_FLAG] if self.is_popup: self.list_editable = () else: self.list_editable = list_editable self.ordering = self.get_ordering(request) self.query = request.GET.get(SEARCH_VAR, '') self.query_set = self.get_query_set(request) self.get_results(request) if self.is_popup: title = ugettext('Select %s') else: title = ugettext('Select %s to change') self.title = title % force_unicode(self.opts.verbose_name) self.pk_attname = self.lookup_opts.pk.attname def get_filters(self, request): lookup_params = self.params.copy() # a dictionary of the query string use_distinct = False # Remove all the parameters that are globally and systematically # ignored. for ignored in IGNORED_PARAMS: if ignored in lookup_params: del lookup_params[ignored] # 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[smart_str(key)] = value if not self.model_admin.lookup_allowed(key, value): raise SuspiciousOperation("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(). 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 def get_query_string(self, new_params=None, remove=None): if new_params is None: new_params = {} if remove is None: remove = [] p = self.params.copy() for r in remove: for k in p.keys(): if k.startswith(r): del p[k] for k, v in new_params.items(): if v is None: if k in p: del p[k] else: p[k] = v return '?%s' % urlencode(p) def get_results(self, request): paginator = self.model_admin.get_paginator(request, self.query_set, self.list_per_page) # Get the number of objects, with admin filters applied. result_count = paginator.count # Get the total number of objects, with no admin filters applied. # Perform a slight optimization: Check to see whether any filters were # given. If not, use paginator.hits to calculate the number of objects, # because we've already done paginator.hits and the value is cached. if not self.query_set.query.where: full_result_count = result_count else: full_result_count = self.root_query_set.count() can_show_all = result_count <= self.list_max_show_all multi_page = result_count > self.list_per_page # Get the list of objects to display on this page. if (self.show_all and can_show_all) or not multi_page: result_list = self.query_set._clone() else: try: result_list = paginator.page(self.page_num + 1).object_list except InvalidPage: raise IncorrectLookupParameters self.result_count = result_count self.full_result_count = full_result_count self.result_list = result_list self.can_show_all = can_show_all self.multi_page = multi_page self.paginator = paginator def _get_default_ordering(self): ordering = [] if self.model_admin.ordering: ordering = self.model_admin.ordering elif self.lookup_opts.ordering: ordering = self.lookup_opts.ordering return ordering def get_ordering_field(self, field_name): """ Returns the proper model field name corresponding to the given field_name to use for ordering. field_name may either be the name of a proper model field or the name of a method (on the admin or model) or a callable with the 'admin_order_field' attribute. Returns None if no proper model field name can be matched. """ try: field = self.lookup_opts.get_field(field_name) return field.name except models.FieldDoesNotExist: # See whether field_name is a name of a non-field # that allows sorting. if callable(field_name): attr = field_name elif hasattr(self.model_admin, field_name): attr = getattr(self.model_admin, field_name) else: attr = getattr(self.model, field_name) return getattr(attr, 'admin_order_field', None) def get_ordering(self, request): params = self.params # For ordering, first check the if exists the "get_ordering" method # in model admin, then check "ordering" parameter in the admin # options, then check the object's default ordering. Finally, a # manually-specified ordering from the query string overrides anything. ordering = self.model_admin.get_ordering( request) or self._get_default_ordering() if ORDER_VAR in params: # Clear ordering and used params ordering = [] order_params = params[ORDER_VAR].split('.') for p in order_params: try: none, pfx, idx = p.rpartition('-') field_name = self.list_display[int(idx)] order_field = self.get_ordering_field(field_name) if not order_field: continue # No 'admin_order_field', skip it ordering.append(pfx + order_field) except (IndexError, ValueError): continue # Invalid ordering specified, skip it. return ordering def get_ordering_field_columns(self): """ Returns a SortedDict of ordering field column numbers and asc/desc """ # We must cope with more than one column having the same underlying sort # field, so we base things on column numbers. ordering = self._get_default_ordering() ordering_fields = SortedDict() if ORDER_VAR not in self.params: # for ordering specified on ModelAdmin or model Meta, we don't know # the right column numbers absolutely, because there might be more # than one column associated with that ordering, so we guess. for field in ordering: if field.startswith('-'): field = field[1:] order_type = 'desc' else: order_type = 'asc' for index, attr in enumerate(self.list_display): if self.get_ordering_field(attr) == field: ordering_fields[index] = order_type break else: for p in self.params[ORDER_VAR].split('.'): none, pfx, idx = p.rpartition('-') try: idx = int(idx) except ValueError: continue # skip it ordering_fields[idx] = 'desc' if pfx == '-' else 'asc' return ordering_fields def get_query_set(self, request): try: # 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 qs 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 # 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 ? from a custom field that raises # yet something else when handed impossible data. 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. if self.ordering: qs = qs.order_by(*self.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 ] for bit in self.query.split(): or_queries = [ models.Q(**{orm_lookup: bit}) for orm_lookup in orm_lookups ] 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
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 ] for bit in self.query.split(): or_queries = [ models.Q(**{orm_lookup: bit}) for orm_lookup in orm_lookups ] 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 def url_for_result(self, result): return "%s/" % quote(getattr(result, self.pk_attname))
class PowerChangeList(ChangeList): def __init__(self, request, model, list_display, list_display_links, list_filter, date_hierarchy, search_fields, list_select_related, list_per_page, list_max_show_all, list_editable, model_admin): #Criando o multi_search_query self.multi_search_query = {} request.GET._mutable = True for k, l, f in model_admin.multi_search: if k in request.GET: self.multi_search_query[k] = request.GET.get(k, '') del request.GET[k] request.GET._mutable = False super(PowerChangeList, self).__init__(request, model, list_display, list_display_links, list_filter, date_hierarchy, search_fields, list_select_related, list_per_page, list_max_show_all, list_editable, model_admin) def get_query_set(self, request): MULTI_SEARCH_VAR = [] for var, label, query in self.model_admin.multi_search: MULTI_SEARCH_VAR.append(var) # 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 #Faz o filter do multi_search if self.multi_search_query: for k_query, query in self.multi_search_query.items(): for k, l, f in self.model_admin.multi_search: if k_query == k: fields = f break if fields and query: orm_lookups = [ construct_search(str(field)) for field in fields ] for bit in query.split(): or_queries = [ models.Q(**{orm_lookup: bit}) for orm_lookup in orm_lookups ] qs = qs.filter(reduce(operator.or_, or_queries)) elif self.search_fields and self.query: orm_lookups = [ construct_search(str(search_field)) for search_field in self.search_fields ] for bit in self.query.split(): or_queries = [ models.Q(**{orm_lookup: bit}) for orm_lookup in orm_lookups ] 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
def get_filters(self, request): lookup_params = self.params.copy() # a dictionary of the query string use_distinct = False # Remove all the parameters that are globally and systematically # ignored. for ignored in IGNORED_PARAMS: if ignored in lookup_params: del lookup_params[ignored] # 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[smart_str(key)] = value if not self.model_admin.lookup_allowed(key, value): raise SuspiciousOperation("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(). 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
def get_filters(self, request): lookup_params = self.params.copy() # a dictionary of the query string use_distinct = False # Remove all the parameters that are globally and systematically # ignored. for ignored in IGNORED_PARAMS: if ignored in lookup_params: del lookup_params[ignored] # 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[smart_str(key)] = value if not self.model_admin.lookup_allowed(key, value): raise SuspiciousOperation("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(). 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
def _queryset(self): (filter_specs, has_filters, use_distinct) = self.filter.get_filters() # Then, we let every list filter modify the queryset to its liking. if self.queryset: qs = self.queryset else: qs = self.model_class.objects.all() for filter_spec in filter_specs: new_qs = filter_spec.queryset(self.request, qs) if new_qs is not None: qs = new_qs # 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.model_class._meta.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() 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 search_query = self.request.GET.get(SEARCH_VAR, '') if self.search_fields and search_query: orm_lookups = [construct_search(str(search_field)) for search_field in self.search_fields] for bit in self.query.split(): or_queries = [models.Q(**{orm_lookup: bit}) for orm_lookup in orm_lookups] 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
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 & 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_object_id(model, generic_field): """ Return the foreign key field for a given GenericForeignKey in a given model """ 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 """ 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: 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): 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>") ids = defaultdict(list) for rel_field, fields in fields_mapping.items(): query = generate_q_object(fields) if not query: logger.warn('No Q instance returned') continue 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: ids[obj_id].extend( model.objects.filter(query).values_list( 'pk', flat=True) ) return ids use_distinct = False if not search_term: return queryset, use_distinct non_generic_fields, generic_fields = parse_related_fields() related_ids = 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] # append generic related filters to or_queries for obj_id, ids_list in related_ids.items(): or_queries.append(Q( **{ '%s__in' % obj_id: ids_list } )) query = 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_filters(self, request): if not request.session.get('use_new_filters'): return super(CustomChangeList, self).get_filters(request) new_filter, created = CustomFilter.objects.get_or_create(user=request.user, model_name=self.model.__name__, app_name=self.model._meta.app_label, default=True) form = CustomFilterForm(request.GET.copy(), custom_filter=new_filter) if len(request.GET) and form.is_valid(): form.save() self.current_filter = CustomFilter.objects.filter(user=request.user, path_info=request.path_info, default=True) # loading filter set params into change list, so they will be applied in queryset if self.current_filter: filter_params, self.exclude_params, self.bundled_params = self.current_filter[0].get_filter_params() self.params.update(**filter_params) lookup_params = self.params.copy() # a dictionary of the query string use_distinct = False # Remove all the parameters that are globally and systematically # ignored. for ignored in IGNORED_PARAMS: if ignored in lookup_params: del lookup_params[ignored] # 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[smart_str(key)] = value if not self.model_admin.lookup_allowed(key, value): raise SuspiciousOperation("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. for key, value in lookup_params.items(): lookup_params[key] = prepare_lookup_value(key, value) try: use_distinct = (use_distinct or lookup_needs_distinct(self.lookup_opts, key)) except FieldDoesNotExist, e: lookup_params.pop(key)