def _check_list_filter_item(self, obj, model, item, label): """ Check one item of `list_filter`, i.e. check if it is one of three options: 1. 'field' -- a basic field filter, possibly w/ relationships (e.g. 'field__rel') 2. ('field', SomeFieldListFilter) - a field-based list filter class 3. SomeListFilter - a non-field list filter class """ from django.contrib.admin import ListFilter, FieldListFilter if callable(item) and not isinstance(item, models.Field): # If item is option 3, it should be a ListFilter... if not issubclass(item, ListFilter): return must_inherit_from(parent='ListFilter', option=label, obj=obj, id='admin.E113') # ... but not a FieldListFilter. elif issubclass(item, FieldListFilter): return [ checks.Error( "The value of '%s' must not inherit from 'FieldListFilter'." % label, hint=None, obj=obj.__class__, id='admin.E114', ) ] else: return [] elif isinstance(item, (tuple, list)): # item is option #2 field, list_filter_class = item if not issubclass(list_filter_class, FieldListFilter): return must_inherit_from(parent='FieldListFilter', option='%s[1]' % label, obj=obj, id='admin.E115') else: return [] else: # item is option #1 field = item # Validate the field string try: get_fields_from_path(model, field) except (NotRelationField, FieldDoesNotExist): return [ checks.Error( "The value of '%s' refers to '%s', which does not refer to a Field." % (label, field), hint=None, obj=obj.__class__, id='admin.E116', ) ] else: return []
def _check_list_filter_item(self, obj, model, item, label): """ Check one item of `list_filter`, i.e. check if it is one of three options: 1. 'field' -- a basic field filter, possibly w/ relationships (e.g. 'field__rel') 2. ('field', SomeFieldListFilter) - a field-based list filter class 3. SomeListFilter - a non-field list filter class """ from django.contrib.admin import ListFilter, FieldListFilter if callable(item) and not isinstance(item, models.Field): # If item is option 3, it should be a ListFilter... if not issubclass(item, ListFilter): return must_inherit_from(parent='ListFilter', option=label, obj=obj, id='admin.E113') # ... but not a FieldListFilter. elif issubclass(item, FieldListFilter): return [ checks.Error( "The value of '%s' must not inherit from 'FieldListFilter'." % label, obj=obj.__class__, id='admin.E114', ) ] else: return [] elif isinstance(item, (tuple, list)): # item is option #2 field, list_filter_class = item if not issubclass(list_filter_class, FieldListFilter): return must_inherit_from(parent='FieldListFilter', option='%s[1]' % label, obj=obj, id='admin.E115') else: return [] else: # item is option #1 field = item # Validate the field string try: get_fields_from_path(model, field) except (NotRelationField, FieldDoesNotExist): return [ checks.Error( "The value of '%s' refers to '%s', which does not refer to a Field." % (label, field), obj=obj.__class__, id='admin.E116', ) ] else: return []
def __init__(self, request, model, field_path, *args, **kwargs): self.field_path = field_path value = kwargs.get('data').get(self.field_path) fields = get_fields_from_path(model, self.field_path) is_relation = fields[0].is_relation url = 'filter_model_autocomplete' if is_relation else 'filter_list_autocomplete' super(AutocompleteForm, self).__init__(*args, **kwargs) url = reverse( url, kwargs={ 'app': model._meta.app_label, 'model': model._meta.model_name, 'field_path': self.field_path } ) if request.GET: url = '%s?%s' % (url, request.GET.urlencode()) if is_relation: related_model = get_model_from_relation(fields[-1]) qs = related_model._default_manager.all() if value: qs = qs.filter(pk=value) self.fields[self.field_path] = forms.ModelChoiceField( queryset=qs, required=False, widget=ModelSelect2(url=url) ) else: choices = [(value, value)] if value else [] self.fields[self.field_path] = forms.ChoiceField( choices=choices, required=False, widget=ListSelect2(url=url), )
def _check_date_hierarchy(self, obj): """ Check that date_hierarchy refers to DateField or DateTimeField. """ if obj.date_hierarchy is None: return [] else: try: field = get_fields_from_path(obj.model, obj.date_hierarchy)[-1] except (NotRelationField, FieldDoesNotExist): return [ checks.Error( "The value of 'date_hierarchy' refers to '%s', which " "does not refer to a Field." % obj.date_hierarchy, obj=obj.__class__, id='admin.E127', ) ] else: if not isinstance(field, (models.DateField, models.DateTimeField)): return must_be('a DateField or DateTimeField', option='date_hierarchy', obj=obj, id='admin.E128') else: return []
def get_widget_instance(self): rel = get_fields_from_path(self.model, self.field_name)[-1].remote_field return AutocompleteSelect( rel, self.model_admin.admin_site, )
def get_filter_for_field(self, f, request): field = get_fields_from_path(self.model, f)[-1] if hasattr(field, 'rel') and hasattr(field.rel, 'to') or hasattr(field, 'remote_field') and hasattr(field.remote_field, 'model'): present_list_filter_fields = getattr(self, 'present_list_filter_fields', []) if f in present_list_filter_fields: return RelatedFieldPresentListFilter return RelatedFieldVisibleListFilter
def get(self, request, model=None, field_name=None): search = request.GET.get('search', '') page = request.GET.get('page', 1) has_next = False if model is field_name is None: return self.render_json_response( {'error': "GetFieldChoices view requires 2 arguments"}, status=400) app_label, model_name = model.split('.', 1) try: model_obj = apps.get_model(app_label, model_name) field = get_fields_from_path(model_obj, field_name)[-1] model_obj = field.model # use new model if followed a ForeignKey except AttributeError as e: logger.debug("Invalid kwargs passed to view: %s", e) return self.render_json_response( {'error': "No installed app/model: %s" % model}, status=400) except (LookupError, FieldDoesNotExist) as e: logger.debug("Invalid kwargs passed to view: %s", e) return self.render_json_response({'error': force_text(e)}, status=400) choices = field.choices choices = sorted(choices) # if no choices, populate with distinct values from instances if not choices: choices = [] disabled = getattr(settings, 'ADVANCED_FILTERS_DISABLE_FOR_FIELDS', tuple()) if field.name in disabled: logger.debug('Skipped lookup of choices for disabled fields') elif isinstance( field, (models.BooleanField, models.DateField, models.TimeField)): logger.debug('No choices calculated for field %s of type %s', field, type(field)) else: # the order_by() avoids ambiguity with values() and distinct() filter_kwargs = { "{}__icontains".format(field.name): search, "{}__isnull".format(field.name): False } queryset = model_obj.objects.filter(**filter_kwargs).order_by( field.name).values_list(field.name, flat=True).distinct() page_size = getattr(settings, 'ADVANCED_FILTERS_PAGE_SIZE', 20) paginator = Paginator(queryset, page_size) try: page = paginator.page(page) choices = zip(page, page) has_next = page.has_next() except EmptyPage: choices = [] has_next = False results = [{'id': c[0], 'text': force_text(c[1])} for c in choices] return self.render_json_response({ 'results': results, "more": has_next })
def get_rendered_widget(self): rel = get_fields_from_path(self.model, self.field_name)[-1].remote_field widget = AutocompleteSelect( rel, self.model_admin.admin_site, ) FieldClass = self.get_form_field() field = FieldClass( queryset=self.get_queryset_for_field(), widget=widget, required=False, ) self._add_media(self.model_admin, widget) attrs = self.widget_attrs.copy() attrs["id"] = "id-%s-autocomplete-filter" % self.field_name attrs["class"] = f'{attrs.get("class", "")} select-filter'.strip() return field.widget.render( name=self.parameter_name, value=self.used_parameters.get(self.parameter_name, ""), attrs=attrs, )
def translate_relation(model, relation): """Transforms Django-style related field lookup (foo__bar) into attrgetter instance that can be called with an object to retrieve the object at the end of defined relation chain. :param relation: Django-style field lookup """ fields = get_fields_from_path(model, relation) last_field = fields[-1] if not issubclass(last_field.related_model, Site): raise ImproperlyConfigured( 'Relation "{}" does not point to Site model (it targets {} instead).' .format( relation, "{}.{}".format( last_field.related_model._meta.app_label, last_field.related_model.__name__, ), )) def get_next_field(obj, field): return getattr(obj, field.name) def inner(obj): obj = reduce(get_next_field, fields, obj) if last_field.many_to_many: return obj.all() return [obj] return inner
def get_filters(self): self.filter_specs = {} for list_filter, *settings in self._filters: try: settings = dict(settings) except ValueError: settings = {} field_path = None field, field_list_filter_class = list_filter, FieldListFilter.create if not isinstance(field, models.Field): # For annotated data you have to specify the type if 'field_type' in settings: # No path for annotated fields field_path = list_filter field = settings['field_type']() else: field_path = field field = get_fields_from_path(self.model, field_path)[-1] spec = field_list_filter_class( field, self.model, field_path=field_path, **settings, ) if spec and spec.has_output(): self.filter_specs[list_filter] = spec return self.filter_specs.values()
def __init__(self, *args, **kwargs): model = kwargs.pop('model') request = kwargs.pop('request') self.field_path = kwargs.pop('field_path') value = kwargs.get('data').get(self.field_path) fields = get_fields_from_path(model, self.field_path) url = 'filter_model_autocomplete' if fields[0].is_relation else 'filter_list_autocomplete' is_relation = url == 'filter_model_autocomplete' if value: choices = [(value, value)] else: choices = [] super(AutocompleteForm, self).__init__(*args, **kwargs) url = reverse( url, kwargs={ 'app': model._meta.app_label, 'model': model._meta.model_name, 'field_path': self.field_path } ) if request.GET: url = '%s?%s' % (url, request.GET.urlencode()) if is_relation: self.fields[self.field_path] = forms.ModelChoiceField( queryset=model._default_manager.none(), required=False, widget=ModelSelect2(url=url) ) else: self.fields[self.field_path] = forms.ChoiceField( choices=choices, required=False, widget=ListSelect2(url=url), )
def __init__(self, field, request, params, hidden_params, model, model_admin, field_path): super(ForeignKeyFilter, self).__init__(field, request, params, hidden_params, model, model_admin, field_path) if self.filter_queryset is None: # check if there is some lookup field in field_path # and get proper QuerySet target_model = model fields = get_fields_from_path(self.model, field_path) if len(fields) > 1: target_model = get_model_from_relation(fields[-2]) self.filter_queryset = getattr(target_model, self.field.name).get_queryset() field = forms.ModelChoiceField( queryset=self.filter_queryset, required=False, empty_label='', widget=autocomplete.ModelSelect2Multiple( url=self.autocomplete_url, )) self._add_media(model_admin) attrs = self.widget_attrs.copy() attrs['id'] = 'id-%s-dal-filter' % self.field_path attrs['data-placeholder'] = self.title rendered_widget = field.widget.render(name=self._parameters[0], value=self._values[0], attrs=attrs) self.context.update({'rendered_widget': rendered_widget})
def get(self, request, app, model, field_path, *args, **kwargs): self.model = self.get_model(app, model) self.fields = get_fields_from_path(self.model, field_path) self.related_model = get_model_from_relation(self.fields[-1]) self.field_path = field_path self.query_string = get_query_string(request.GET, remove=[field_path, 'q']) return super(FilterModelAutocomplete, self).get(request, *args, **kwargs)
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_related_field_ajax_list_filter_params(self): model = RelatedToTestModel field_path = 'field' field = get_fields_from_path(model, field_path)[-1] lookup_params = {} model_admin = admin.site._registry.get(model) return field, lookup_params, model, model_admin, field_path
def validate_list_filter(self, cls, model): """ Validate that list_filter is a sequence of one of three options: 1: 'field' - a basic field filter, possibly w/ relationships (eg, 'field__rel') 2: ('field', SomeFieldListFilter) - a field-based list filter class 3: SomeListFilter - a non-field list filter class """ from django.contrib.admin import ListFilter, FieldListFilter if hasattr(cls, 'list_filter'): check_isseq(cls, 'list_filter', cls.list_filter) for idx, item in enumerate(cls.list_filter): if callable(item) and not isinstance(item, models.Field): # If item is option 3, it should be a ListFilter... if not issubclass(item, ListFilter): raise ImproperlyConfigured( "'%s.list_filter[%d]' is '%s'" " which is not a descendant of ListFilter." % (cls.__name__, idx, item.__name__)) # ... but not a FieldListFilter. if issubclass(item, FieldListFilter): raise ImproperlyConfigured( "'%s.list_filter[%d]' is '%s'" " which is of type FieldListFilter but is not" " associated with a field name." % (cls.__name__, idx, item.__name__)) else: if isinstance(item, (tuple, list)): # item is option #2 field, list_filter_class = item if not issubclass(list_filter_class, FieldListFilter): raise ImproperlyConfigured( "'%s.list_filter[%d][1]'" " is '%s' which is not of type FieldListFilter." % (cls.__name__, idx, list_filter_class.__name__)) else: # item is option #1 field = item # Validate the field string try: get_fields_from_path(model, field) except (NotRelationField, FieldDoesNotExist): raise ImproperlyConfigured( "'%s.list_filter[%d]' refers to '%s'" " which does not refer to a Field." % (cls.__name__, idx, field))
def get_field_by_relation_path(model, field_path): """ Returns field for `model` referenced by `field_path`. E.g. calling: get_field_by_relation_path(BackOfficeAsset, 'model__manufacturer__name') returns: <django.db.models.fields.CharField: name> """ return get_fields_from_path(model, field_path)[-1]
def get_related_field_ajax_list_filter_params(self): class ModelAdmin(admin.ModelAdmin): pass model = RelatedToTestModel field_path = 'field' field = get_fields_from_path(model, field_path)[-1] lookup_params = {} model_admin = ModelAdmin return field, lookup_params, model, model_admin, field_path
def get(self, request, model=None, field_name=None): if model is field_name is None: return self.render_json_response( {'error': "GetOperatorChoices view requires 2 arguments"}, status=400) app_label, model_name = model.split('.', 1) try: model_obj = apps.get_model(app_label, model_name) field = get_fields_from_path(model_obj, field_name)[-1] model_obj = field.model internal_type = field.get_internal_type() disabled = getattr(settings, 'ADVANCED_FILTERS_DISABLE_FOR_FIELDS', tuple()) if field.name in disabled: logger.debug('Skipped lookup of operators for disabled fields') choices = [] else: af_options = dict(AdvancedFilterQueryForm.OPERATORS) choices = [] field_options = [] if (internal_type == 'CharField' or internal_type == 'EmailField' or internal_type == 'URLField'): field_options = ["iexact", "icontains", "iregex", "isnull"] elif internal_type == 'BooleanField': field_options = ["istrue", "isfalse", "isnull"] elif (internal_type == 'PositiveIntegerField' or internal_type == 'SmallIntegerField' or internal_type == 'PositiveSmallIntegerField' or internal_type == 'BigIntegerField' or internal_type == 'IntegerField' or internal_type == 'FloatField' or internal_type == 'DecimalField'): field_options = ["lt", "gt", "lte", "gte", "isnull"] elif (internal_type == 'DateTimeField' or internal_type == 'DateField'): field_options = [ "range", "lt", "gt", "lte", "gte", "isnull" ] else: field_options = af_options choices = [{ 'key': option, 'value': af_options[option] } for option in field_options] return self.render_json_response({'results': choices}) except AttributeError as e: logger.debug("Invalid kwargs passed to view: %s", e) return self.render_json_response( {'error': "No installed app/model: %s" % model}, status=400) except (LookupError, FieldDoesNotExist) as e: logger.debug("Invalid kwargs passed to view: %s", e) return self.render_json_response({'error': force_text(e)}, status=400)
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 validate_list_filter(self, cls, model): """ Validate that list_filter is a sequence of one of three options: 1: 'field' - a basic field filter, possibly w/ relationships (eg, 'field__rel') 2: ('field', SomeFieldListFilter) - a field-based list filter class 3: SomeListFilter - a non-field list filter class """ from django.contrib.admin import ListFilter, FieldListFilter if hasattr(cls, 'list_filter'): check_isseq(cls, 'list_filter', cls.list_filter) for idx, item in enumerate(cls.list_filter): if callable(item) and not isinstance(item, models.Field): # If item is option 3, it should be a ListFilter... if not issubclass(item, ListFilter): raise ImproperlyConfigured("'%s.list_filter[%d]' is '%s'" " which is not a descendant of ListFilter." % (cls.__name__, idx, item.__name__)) # ... but not a FieldListFilter. if issubclass(item, FieldListFilter): raise ImproperlyConfigured("'%s.list_filter[%d]' is '%s'" " which is of type FieldListFilter but is not" " associated with a field name." % (cls.__name__, idx, item.__name__)) else: if isinstance(item, (tuple, list)): # item is option #2 field, list_filter_class = item if not issubclass(list_filter_class, FieldListFilter): raise ImproperlyConfigured("'%s.list_filter[%d][1]'" " is '%s' which is not of type FieldListFilter." % (cls.__name__, idx, list_filter_class.__name__)) else: # item is option #1 field = item # Validate the field string try: get_fields_from_path(model, field) except (NotRelationField, FieldDoesNotExist): raise ImproperlyConfigured("'%s.list_filter[%d]' refers to '%s'" " which does not refer to a Field." % (cls.__name__, idx, field))
def get(self, request, model=None, field_name=None): if model is field_name is None: return self.render_json_response( {'error': "GetFieldChoices view requires 2 arguments"}, status=400) app_label, model_name = model.split('.', 1) try: model_obj = apps.get_model(app_label, model_name) field = get_fields_from_path(model_obj, field_name)[-1] model_obj = field.model # use new model if followed a ForeignKey except AttributeError as e: logger.debug("Invalid kwargs passed to view: %s", e) return self.render_json_response( {'error': "No installed app/model: %s" % model}, status=400) except (LookupError, FieldDoesNotExist) as e: logger.debug("Invalid kwargs passed to view: %s", e) return self.render_json_response({'error': force_str(e)}, status=400) choices = field.choices # if no choices, populate with distinct values from instances if not choices: choices = [] disabled = getattr(settings, 'ADVANCED_FILTERS_DISABLE_FOR_FIELDS', tuple()) max_choices = getattr(settings, 'ADVANCED_FILTERS_MAX_CHOICES', 254) if field.name in disabled: logger.debug('Skipped lookup of choices for disabled fields') elif isinstance( field, (models.BooleanField, models.DateField, models.TimeField)): logger.debug('No choices calculated for field %s of type %s', field, type(field)) else: # the order_by() avoids ambiguity with values() and distinct() choices = model_obj.objects.order_by(field.name).values_list( field.name, flat=True).distinct() # additional query is ok to avoid fetching too many values if choices.count() <= max_choices: choices = zip(choices, choices) logger.debug('Choices found for field %s: %s', field.name, choices) else: choices = [] results = [{ 'id': c[0], 'text': force_str(c[1]) } for c in sorted(choices, key=lambda x: (x[0] is not None, x[0]))] return self.render_json_response({'results': results})
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 get_field_title_by_relation_path(model, field_path): """ Return field verbose name If field path is nested (using __), returned name is one before last field verbose name. """ fields = get_fields_from_path(model, field_path) if len(fields) > 1: field = fields[-2] else: field = fields[-1] return field.verbose_name
def _parse_query_dict(query_data, model): """ Take a list of query field dict and return data for form initialization """ operator = 'iexact' if query_data['field'] == '_OR': query_data['operator'] = operator return query_data parts = query_data['field'].split('__') if len(parts) < 2: field = parts[0] else: if parts[-1] in dict(AdvancedFilterQueryForm.OPERATORS).keys(): field = '__'.join(parts[:-1]) operator = parts[-1] else: field = query_data['field'] query_data['field'] = field mfield = get_fields_from_path(model, query_data['field']) if not mfield: raise Exception( 'Field path "%s" could not be followed to a field' ' in model %s', query_data['field'], model) else: mfield = mfield[-1] # get the field object if query_data['value'] is None: query_data['operator'] = "isnull" elif query_data['value'] is True: query_data['operator'] = "istrue" elif query_data['value'] is False: query_data['operator'] = "isfalse" else: if isinstance(mfield, DateField): # this is a date/datetime field query_data['operator'] = "range" # default else: query_data['operator'] = operator # default if isinstance(query_data.get('value'), list) and query_data['operator'] == 'range': dtfrom = dt.fromtimestamp(query_data.get('value_from', 0)) dtto = dt.fromtimestamp(query_data.get('value_to', 0)) query_data['value'] = ','.join( [dtfrom.strftime('%Y-%m-%d'), dtto.strftime('%Y-%m-%d')]) return query_data
def get_list_display_columns(cls) -> List[str]: """Returns the column names for this model for use in the table""" display_columns = [] for fname in cls.list_display: try: field = get_fields_from_path(cls, fname)[-1] display_columns.append(field.verbose_name) except FieldDoesNotExist: # Field is property/method display_columns.append(fname.replace("_", " ")) return display_columns
def get_model_field(model, field_name): """ Returns the `Field` instance belonging to `field_name` on a `Model` instance or class. This works for related fields. Example:: >>> get_model_field(Zaak, 'zaaktype__zaaktypeomschrijving') <django.db.models.fields.CharField: zaaktypeomschrijving> """ from zaakmagazijn.rgbz_mapping.base import ModelProxy if issubclass(model, ModelProxy): return model.get_field(field_name) return get_fields_from_path(model, field_name)[-1]
def get(self, request, model=None, field_name=None): if model is field_name is None: return self.render_json_response( {'error': "GetFieldChoices view requires 2 arguments"}, status=400) app_label, model_name = model.split('.', 1) try: model_obj = apps.get_model(app_label, model_name) field = get_fields_from_path(model_obj, field_name)[-1] model_obj = field.model # use new model if followed a ForeignKey except AttributeError as e: logger.debug("Invalid kwargs passed to view: %s", e) return self.render_json_response( {'error': "No installed app/model: %s" % model}, status=400) except (LookupError, FieldDoesNotExist) as e: logger.debug("Invalid kwargs passed to view: %s", e) return self.render_json_response( {'error': force_text(e)}, status=400) choices = field.choices # if no choices, populate with distinct values from instances if not choices: choices = [] disabled = getattr(settings, 'ADVANCED_FILTERS_DISABLE_FOR_FIELDS', tuple()) max_choices = getattr(settings, 'ADVANCED_FILTERS_MAX_CHOICES', 254) if field.name in disabled: logger.debug('Skipped lookup of choices for disabled fields') elif isinstance(field, (models.BooleanField, models.DateField, models.TimeField)): logger.debug('No choices calculated for field %s of type %s', field, type(field)) else: # the order_by() avoids ambiguity with values() and distinct() choices = model_obj.objects.order_by(field.name).values_list( field.name, flat=True).distinct() # additional query is ok to avoid fetching too many values if choices.count() <= max_choices: choices = zip(choices, choices) logger.debug('Choices found for field %s: %s', field.name, choices) else: choices = [] results = [{'id': c[0], 'text': force_text(c[1])} for c in sorted( choices, key=itemgetter(0))] return self.render_json_response({'results': results})
def _parse_query_dict(query_data, model): """ Take a list of query field dict and return data for form initialization """ operator = 'iexact' if query_data['field'] == '_OR': query_data['operator'] = operator return query_data parts = query_data['field'].split('__') if len(parts) < 2: field = parts[0] else: if parts[-1] in dict(AdvancedFilterQueryForm.OPERATORS).keys(): field = '__'.join(parts[:-1]) operator = parts[-1] else: field = query_data['field'] query_data['field'] = field mfield = get_fields_from_path(model, query_data['field']) if not mfield: raise Exception('Field path "%s" could not be followed to a field' ' in model %s', query_data['field'], model) else: mfield = mfield[-1] # get the field object if query_data['value'] is None: query_data['operator'] = "isnull" elif query_data['value'] is True: query_data['operator'] = "istrue" elif query_data['value'] is False: query_data['operator'] = "isfalse" else: if isinstance(mfield, DateField): # this is a date/datetime field query_data['operator'] = "range" # default else: query_data['operator'] = operator # default if isinstance(query_data.get('value'), list) and query_data['operator'] == 'range': dtfrom = dt.fromtimestamp(query_data.get('value_from', 0)) dtto = dt.fromtimestamp(query_data.get('value_to', 0)) query_data['value'] = ','.join([dtfrom.strftime('%Y-%m-%d'), dtto.strftime('%Y-%m-%d')]) return query_data
def get_filter_classes(self): lookup_params = self.get_data() # 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["{}".format(key)] = value request = FakeRequest(self.request, GET=self.get_data()) filter_specs = [] list_filters = self.get_list_filters() 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, 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): from django.contrib.admin.utils import get_fields_from_path 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, field_path=field_path) if spec and spec.has_output(): filter_specs.append(spec) return filter_specs
def get_field_values(self, field_names: List[str]) -> List[Tuple[str, Any]]: """Returns a tuple of field names and values for the given fields""" field_values = [] for fname in field_names: try: field_values.append( ( get_fields_from_path(self._meta.model, fname)[-1].verbose_name, get_field_value(self, fname), ) ) except FieldDoesNotExist: value = getattr(self, fname) if callable(value): value = value() field_values.append((fname, value)) return field_values
def get_fields_from_model(self, model, fields): """ Iterate over given <field> names (in "orm query" notation) and find the actual field given the initial <model>. If <field> is a tuple of the format ('field_name', 'Verbose name'), overwrite the field's verbose name with the given name for display purposes. """ model_fields = {} for field in fields: if isinstance(field, tuple) and len(field) == 2: field, verbose_name = field[0], field[1] else: try: model_field = get_fields_from_path(model, field)[-1] verbose_name = model_field.verbose_name except (FieldDoesNotExist, IndexError, TypeError) as e: logger.warn("AdvancedFilterForm: skip invalid field " "- %s", e) continue model_fields[field] = verbose_name return model_fields
# -*- coding: utf-8 -*-
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_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 date_hierarchy(cl): """ Displays the date hierarchy for date drill-down functionality. """ if cl.date_hierarchy: field_name = cl.date_hierarchy field = get_fields_from_path(cl.model, field_name)[-1] dates_or_datetimes = 'datetimes' if isinstance(field, models.DateTimeField) else 'dates' year_field = '%s__year' % field_name month_field = '%s__month' % field_name day_field = '%s__day' % field_name field_generic = '%s__' % field_name year_lookup = cl.params.get(year_field) month_lookup = cl.params.get(month_field) day_lookup = cl.params.get(day_field) link = lambda filters: cl.get_query_string(filters, [field_generic]) if not (year_lookup or month_lookup or day_lookup): # select appropriate start level date_range = cl.queryset.aggregate(first=models.Min(field_name), last=models.Max(field_name)) if date_range['first'] and date_range['last']: if date_range['first'].year == date_range['last'].year: year_lookup = date_range['first'].year if date_range['first'].month == date_range['last'].month: month_lookup = date_range['first'].month if year_lookup and month_lookup and day_lookup: day = datetime.date(int(year_lookup), int(month_lookup), int(day_lookup)) return { 'field_name': field.verbose_name, 'show': True, 'back': { 'link': link({year_field: year_lookup, month_field: month_lookup}), 'title': capfirst(formats.date_format(day, 'YEAR_MONTH_FORMAT')) }, 'choices': [{'title': capfirst(formats.date_format(day, 'MONTH_DAY_FORMAT'))}] } elif year_lookup and month_lookup: days = cl.queryset.filter(**{year_field: year_lookup, month_field: month_lookup}) days = getattr(days, dates_or_datetimes)(field_name, 'day') return { 'show': True, 'field_name': field.verbose_name, 'current_filter': capfirst(MONTHS.get(int(month_lookup), 'UNKNOWN')), 'back': { 'link': link({year_field: year_lookup}), 'title': str(year_lookup) }, 'choices': [{ 'link': link({year_field: year_lookup, month_field: month_lookup, day_field: day.day}), 'title': capfirst(formats.date_format(day, 'MONTH_DAY_FORMAT')) } for day in days] } elif year_lookup: months = cl.queryset.filter(**{year_field: year_lookup}) months = getattr(months, dates_or_datetimes)(field_name, 'month') return { 'show': True, 'field_name': field.verbose_name, 'current_filter': year_lookup, 'back': { 'link': link({}), 'title': _('All dates') }, 'choices': [{ 'link': link({year_field: year_lookup, month_field: month.month}), 'title': capfirst(formats.date_format(month, 'YEAR_MONTH_FORMAT')) } for month in months] } else: years = getattr(cl.queryset, dates_or_datetimes)(field_name, 'year') return { 'show': True, 'current_filter': _('All'), 'field_name': field.verbose_name, 'choices': [{ 'link': link({year_field: str(year.year)}), 'title': str(year.year), } for year in years] }
def date_hierarchy(cl): """Displays the date hierarchy for date drill-down functionality. This tag overrides Django Admin date_hierarchy template tag at django/contrib/admin/templatetags/admin_list.py -> date_hierarchy The tag prevents additional queries used for generating the date hierarchy. The default tag performs a query on the filtered queryset to find the dates for which there is data for the level in the hierarchy. On large tables this query can be very expensive. The additional query is prevented by setting date_hierarchy_drilldown = False on the model admin. When drilldown is disabled the tag will generate a default range of dates based only on the selected hierarchy level without performing a query. Hierarchy levels: Month - all days of the month. Year - All months of year. None - +-3 years from current year. When date_hierarchy_drilldown = True or when not set the default behaviour is preserved. Usage: class MyModelAdmin(admin.ModelAdmin): date_hierarchy = 'created' date_hierarchy_drilldown = False """ if cl.date_hierarchy: field_name = cl.date_hierarchy field = get_fields_from_path(cl.model, field_name)[-1] dates_or_datetimes = 'datetimes' if isinstance(field, models.DateTimeField) else 'dates' year_field = '%s__year' % field_name month_field = '%s__month' % field_name day_field = '%s__day' % field_name field_generic = '%s__' % field_name year_lookup = cl.params.get(year_field) month_lookup = cl.params.get(month_field) day_lookup = cl.params.get(day_field) def link(filters): return cl.get_query_string(filters, [field_generic]) date_hierarchy_drilldown = getattr(cl.model_admin, 'date_hierarchy_drilldown', True) date_hierarchy_drilldown_fn = getattr( cl.model_admin, 'get_date_hierarchy_drilldown', default_date_hierarchy_drilldown, ) if not (year_lookup or month_lookup or day_lookup): # Select appropriate start level. if date_hierarchy_drilldown: date_range = cl.queryset.aggregate(first=models.Min(field_name), last=models.Max(field_name)) if date_range['first'] and date_range['last']: if date_range['first'].year == date_range['last'].year: year_lookup = date_range['first'].year if date_range['first'].month == date_range['last'].month: month_lookup = date_range['first'].month if year_lookup and month_lookup and day_lookup: day = datetime.date(int(year_lookup), int(month_lookup), int(day_lookup)) return { 'show': True, 'back': { 'link': link({year_field: year_lookup, month_field: month_lookup}), 'title': capfirst(formats.date_format(day, 'YEAR_MONTH_FORMAT')) }, 'choices': [{'title': capfirst(formats.date_format(day, 'MONTH_DAY_FORMAT'))}] } elif year_lookup and month_lookup: if date_hierarchy_drilldown: days = cl.queryset.filter(**{year_field: year_lookup, month_field: month_lookup}) days = getattr(days, dates_or_datetimes)(field_name, 'day') else: days = date_hierarchy_drilldown_fn(int(year_lookup), int(month_lookup)) return { 'show': True, 'back': { 'link': link({year_field: year_lookup}), 'title': str(year_lookup) }, 'choices': [{ 'link': link({year_field: year_lookup, month_field: month_lookup, day_field: day.day}), 'title': capfirst(formats.date_format(day, 'MONTH_DAY_FORMAT')) } for day in days] } elif year_lookup: if date_hierarchy_drilldown: months = cl.queryset.filter(**{year_field: year_lookup}) months = getattr(months, dates_or_datetimes)(field_name, 'month') else: months = date_hierarchy_drilldown_fn(int(year_lookup), None) return { 'show': True, 'back': { 'link': link({}), 'title': _('All dates') }, 'choices': [{ 'link': link({year_field: year_lookup, month_field: month.month}), 'title': capfirst(formats.date_format(month, 'YEAR_MONTH_FORMAT')) } for month in months] } else: if date_hierarchy_drilldown: years = getattr(cl.queryset, dates_or_datetimes)(field_name, 'year') else: years = date_hierarchy_drilldown_fn(None, None) return { 'show': True, 'choices': [{ 'link': link({year_field: str(year.year)}), 'title': str(year.year), } for year in years] }
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 date_hierarchy(cl): """ Displays the date hierarchy for date drill-down functionality. """ if cl.date_hierarchy: field_name = cl.date_hierarchy field = get_fields_from_path(cl.model, field_name)[-1] dates_or_datetimes = 'datetimes' if isinstance( field, models.DateTimeField) else 'dates' year_field = '%s__year' % field_name month_field = '%s__month' % field_name day_field = '%s__day' % field_name field_generic = '%s__' % field_name year_lookup = cl.params.get(year_field) month_lookup = cl.params.get(month_field) day_lookup = cl.params.get(day_field) def link(filters): return cl.get_query_string(filters, [field_generic]) if not (year_lookup or month_lookup or day_lookup): # select appropriate start level date_range = cl.queryset.aggregate(first=models.Min(field_name), last=models.Max(field_name)) if date_range['first'] and date_range['last']: if date_range['first'].year == date_range['last'].year: year_lookup = date_range['first'].year if date_range['first'].month == date_range['last'].month: month_lookup = date_range['first'].month if year_lookup and month_lookup and day_lookup: day = datetime.date(int(year_lookup), int(month_lookup), int(day_lookup)) return { 'show': True, 'back': { 'link': link({ year_field: year_lookup, month_field: month_lookup }), 'title': capfirst(formats.date_format(day, 'YEAR_MONTH_FORMAT')) }, 'choices': [{ 'title': capfirst(formats.date_format(day, 'MONTH_DAY_FORMAT')) }] } elif year_lookup and month_lookup: days = cl.queryset.filter(**{ year_field: year_lookup, month_field: month_lookup }) days = getattr(days, dates_or_datetimes)(field_name, 'day') return { 'show': True, 'back': { 'link': link({year_field: year_lookup}), 'title': str(year_lookup) }, 'choices': [{ 'link': link({ year_field: year_lookup, month_field: month_lookup, day_field: day.day }), 'title': capfirst(formats.date_format(day, 'MONTH_DAY_FORMAT')) } for day in days] } elif year_lookup: months = cl.queryset.filter(**{year_field: year_lookup}) months = getattr(months, dates_or_datetimes)(field_name, 'month') return { 'show': True, 'back': { 'link': link({}), 'title': _('All dates') }, 'choices': [{ 'link': link({ year_field: year_lookup, month_field: month.month }), 'title': capfirst(formats.date_format(month, 'YEAR_MONTH_FORMAT')) } for month in months] } else: years = getattr(cl.queryset, dates_or_datetimes)(field_name, 'year') return { 'show': True, 'choices': [{ 'link': link({year_field: str(year.year)}), 'title': str(year.year), } for year in years] }
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_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