def validate(cls, model): """ Does basic ModelAdmin option validation. Calls custom validation classmethod in the end if it is provided in cls. The signature of the custom validation classmethod should be: def validate(cls, model). """ # Before we can introspect models, they need to be fully loaded so that # inter-relations are set up correctly. We force that here. models.get_apps() opts = model._meta validate_base(cls, model) # list_display if hasattr(cls, 'list_display'): check_isseq(cls, 'list_display', cls.list_display) for idx, field in enumerate(cls.list_display): if not callable(field): if not hasattr(cls, field): if not hasattr(model, field): try: opts.get_field(field) except models.FieldDoesNotExist: raise ImproperlyConfigured("%s.list_display[%d], %r is not a callable or an attribute of %r or found in the model %r." % (cls.__name__, idx, field, cls.__name__, model._meta.object_name)) else: # getattr(model, field) could be an X_RelatedObjectsDescriptor f = fetch_attr(cls, model, opts, "list_display[%d]" % idx, field) if isinstance(f, models.ManyToManyField): raise ImproperlyConfigured("'%s.list_display[%d]', '%s' is a ManyToManyField which is not supported." % (cls.__name__, idx, field)) # list_display_links if hasattr(cls, 'list_display_links'): check_isseq(cls, 'list_display_links', cls.list_display_links) for idx, field in enumerate(cls.list_display_links): fetch_attr(cls, model, opts, 'list_display_links[%d]' % idx, field) if field not in cls.list_display: raise ImproperlyConfigured("'%s.list_display_links[%d]'" "refers to '%s' which is not defined in 'list_display'." % (cls.__name__, idx, field)) # list_filter if hasattr(cls, 'list_filter'): check_isseq(cls, 'list_filter', cls.list_filter) for idx, fpath in enumerate(cls.list_filter): try: get_fields_from_path(model, fpath) except (NotRelationField, FieldDoesNotExist), e: raise ImproperlyConfigured( "'%s.list_filter[%d]' refers to '%s' which does not refer to a Field." % ( cls.__name__, idx, fpath ) )
def addQbeFilterStmt(sFilter, model, JsonField): """ Verifica casos especiales y obtiene el QStmt retorna un objeto Q """ fieldName = sFilter['property'].replace('.', '__') if fieldName.endswith('__pk') or fieldName.endswith('_id') or fieldName == 'pk': # Los id por ahora son numericos sType = 'int' elif fieldName == '__str__': # El campo especial __str__ debe ser descompuesto en los seachFields en forma explicita return Q() elif fieldName.startswith(JsonField + '__'): sType = 'string' else: try: # Obtiene el tipo de dato, si no existe la col retorna elimina la condicion field = get_fields_from_path(model, fieldName)[-1] sType = TypeEquivalence.get(field.__class__.__name__, 'string') except : return Q() QStmt = getQbeStmt(fieldName , sFilter['filterStmt'], sType) return QStmt
def __init__(self, request, model, filter_name): self.request = request self.model = model self.field = get_fields_from_path(model, filter_name)[-1] self.name = filter_name self.type = None # Determine the filter type. # Relations if (hasattr(self.field, 'rel') and bool(self.field.rel) or isinstance(self.field, models.related.RelatedObject)): self.type = 'relation' # Boolean fields if (isinstance(self.field, models.BooleanField) or isinstance(self.field, models.NullBooleanField)): self.type = 'boolean' # Choice fields if bool(self.field.choices): self.type = 'choice' # Parse value value = self.request.GET.get(self.name, None) try: if self.type == 'relation': self.value = long(value) elif self.type == 'boolean': self.value = bool(value) else: self.value = value except (ValueError, TypeError): self.value = value
def get_filters(self, request, use_distinct=False): filter_specs = [] cleaned_params, use_distinct = self.get_lookup_params(use_distinct) if self.list_filter: for list_filer in self.list_filter: if callable(list_filer): # This is simply a custom list filter class. spec = list_filer(request, cleaned_params, self.model, self.model_admin) else: field_path = None if isinstance(list_filer, (tuple, list)): # This is a custom FieldListFilter class for a given field. field, field_list_filter_class = list_filer 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_filer, 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, cleaned_params, self.model, self.model_admin, field_path=field_path) if spec and spec.has_output(): filter_specs.append(spec) return filter_specs, bool(filter_specs)
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 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 get_headers(self): output = [Header(self, 0, capfirst( get_fields_from_path(self.model, field)[-1].verbose_name)) for field in self.selected_group_by] ind = len(output) for title in self.annotate_titles: output.append(Header(self, ind, title)) ind += 1 return output
def get_filters(self, request): filter_specs = [] if self.list_filter: for filter_name in self.list_filter: field = get_fields_from_path(self.model, filter_name)[-1] spec = FilterSpec.create(field, request, self.params, self.model, self.model_admin, field_path=filter_name) if spec and spec.has_output(): filter_specs.append(spec) return filter_specs, bool(filter_specs)
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_filters(self, request): if self.list_filter: new_list = [] for i, list_filter in enumerate(self.list_filter): new_list.append(list_filter) if isinstance(list_filter, basestring): field = get_fields_from_path(self.model, list_filter)[-1] if hasattr(field, 'choices'): new_list[i] = list_filter self.list_filter = new_list return super(IChangeList, self).get_filters(request)
def get_result_headers(self): output = {} if self.grouper.has_output(): for field_name in self.grouper.group_value: output[field_name] = capfirst(get_fields_from_path(self.model, field_name)[-1].verbose_name) annotate, self.annotate_titles = self.split_annotate_titles(self.annotate) self.annotate_fields = self.get_annotate_fields(annotate) func = lambda x: x[0] annotate_map = dict(zip(map(func, self.annotate_fields), self.annotate_titles)) output.update(annotate_map) return output
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 get_filters(self, model_admin): filter_specs = [] if self.list_filter: #fields = [] for field_name in self.list_filter: try: field = get_fields_from_path(model_admin.model, field_name)[-1] #~ field = self.get_field(field_name) except: filter_specs.append(LookupFilterSpec(field_name, self.request, self.params, self.model, model_admin)) continue spec = FilterSpec.create(field, self.request, self.params, self.model, model_admin, field_name) if spec and spec.has_output(): filter_specs.append(spec) return filter_specs, bool(filter_specs)
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 = models.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: choices = model_obj.objects.values_list(field.name, flat=True) if choices.count() < max_choices: choices = set(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 """ if query_data['field'] == '_OR': query_data['operator'] = 'iexact' 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]) 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'] = "iexact" # 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 _parse_query_dict(query_data, model): """ Take a list of query field dict and return data for form initialization """ if query_data["field"] == "_OR": query_data["operator"] = "iexact" 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]) 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"] = "iexact" # 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 getTextSearchFields( pSearchFields, model ) : # TODO: Esto deberia ser un parametro configurable en la pci textSearchFlds = [] textFilterTypes = [ 'CharField', 'TextField', 'IntegerField', 'DecimalField', 'FloatField', ] for fName in pSearchFields: try: # Busca el campo en el modelo y joins field = get_fields_from_path( model, fName)[-1] #field = model._meta.get_field( fName ) #model = field.rel.to #model.famille.field.related.parent_model except: continue if field.__class__.__name__ in textFilterTypes: textSearchFlds.append( fName ) return textSearchFlds
def get_filters(self, request): # Esto lo modificamos para que en el filtro solo aparezcan opciones con # datos (por ejemplo, en la vista dispositivos solo aparezcan en el # filtro de localidad las localidades que tienen dispositivos filter_specs = [] if self.list_filter: for filter_name in self.list_filter: field = get_fields_from_path(self.model, filter_name)[-1] spec = FilterSpec.create(field, request, self.params, self.model, self.model_admin, field_path=filter_name) #Salva: if isinstance(spec, RelatedFilterSpec) and not '__' in filter_name: spec.lookup_choices = [(x._get_pk_val(),x.__unicode__()) for x in field.rel.to.objects.annotate(num_rel=Count(self.model._meta.verbose_name)).filter(num_rel__gt=0)] #IGNORE:W0212 #Fin Modificacion if spec and spec.has_output(): filter_specs.append(spec) return filter_specs, bool(filter_specs)
def get_specs(request, model, model_admin): filter_specs = [] if model_admin.list_filter: for list_filter in model_admin.list_filter: if callable(list_filter): spec = list_filter(request, [], model, model_admin) else: field_path = None if isinstance(list_filter, (tuple, list)): field, field_list_filter_class = list_filter else: field, field_list_filter_class = (list_filter, FieldListFilter.create) if not isinstance(field, models.Field): field_path = field field = get_fields_from_path(model, field_path)[-1] spec = field_list_filter_class(field, request, [], model, model_admin, field_path=field_path) if spec and spec.has_output(): filter_specs.append(spec) return filter_specs
def get(self, request, model, field_name): app_label, model_name = model.split('.', 1) model_obj = models.get_model(app_label, model_name) if not model_obj: return self.render_json_response( {'error': 'No model found for %s' % model}, status=400) try: field = get_fields_from_path(model_obj, field_name)[-1] model_obj = field.model # use new model if followed a ForeignKey except FieldDoesNotExist: return self.render_json_response( {'error': 'No such field %s found in model %s' % (field_name, model)}, status=400) choices = field.choices # if no choices, populate with distinct values from instances if not choices: choices = [] if field.name in DISABLE_FOR_FIELDS: 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: choices = set(model_obj.objects.values_list( field.name, flat=True)) if len(choices) < MAX_CHOICES: choices = zip(choices, choices) logger.debug('Choices found for field %s: %s', field.name, pformat(choices)) else: choices = [] results = [{'id': c[0], 'text': unicode(c[1])} for c in sorted( choices, key=itemgetter(0))] return self.render_json_response({'results': results})
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
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 __init__(self, f, request, params, model, model_admin): field = get_fields_from_path(model, f)[-1] FilterSpec.__init__(self, field, request, params, model, model_admin, field_path=f) self.model = model self.lookup_val = request.GET.get(f, None)
def validate(cls, model): """ Does basic ModelAdmin option validation. Calls custom validation classmethod in the end if it is provided in cls. The signature of the custom validation classmethod should be: def validate(cls, model). """ # Before we can introspect models, they need to be fully loaded so that # inter-relations are set up correctly. We force that here. models.get_apps() opts = model._meta validate_base(cls, model) # list_display if hasattr(cls, 'list_display'): check_isseq(cls, 'list_display', cls.list_display) for idx, field in enumerate(cls.list_display): if not callable(field): if not hasattr(cls, field): if not hasattr(model, field): try: opts.get_field(field) except models.FieldDoesNotExist: raise ImproperlyConfigured("%s.list_display[%d], %r is not a callable or an attribute of %r or found in the model %r." % (cls.__name__, idx, field, cls.__name__, model._meta.object_name)) else: # getattr(model, field) could be an X_RelatedObjectsDescriptor f = fetch_attr(cls, model, opts, "list_display[%d]" % idx, field) if isinstance(f, models.ManyToManyField): raise ImproperlyConfigured("'%s.list_display[%d]', '%s' is a ManyToManyField which is not supported." % (cls.__name__, idx, field)) # list_display_links if hasattr(cls, 'list_display_links'): check_isseq(cls, 'list_display_links', cls.list_display_links) for idx, field in enumerate(cls.list_display_links): if field not in cls.list_display: raise ImproperlyConfigured("'%s.list_display_links[%d]' " "refers to '%s' which is not defined in 'list_display'." % (cls.__name__, idx, field)) # list_filter if hasattr(cls, 'list_filter'): check_isseq(cls, 'list_filter', cls.list_filter) for idx, item in enumerate(cls.list_filter): # There are three options for specifying a filter: # 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 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)) # list_per_page = 100 if hasattr(cls, 'list_per_page') and not isinstance(cls.list_per_page, int): raise ImproperlyConfigured("'%s.list_per_page' should be a integer." % cls.__name__) # list_max_show_all if hasattr(cls, 'list_max_show_all') and not isinstance(cls.list_max_show_all, int): raise ImproperlyConfigured("'%s.list_max_show_all' should be an integer." % cls.__name__) # list_editable if hasattr(cls, 'list_editable') and cls.list_editable: check_isseq(cls, 'list_editable', cls.list_editable) for idx, field_name in enumerate(cls.list_editable): try: field = opts.get_field_by_name(field_name)[0] except models.FieldDoesNotExist: raise ImproperlyConfigured("'%s.list_editable[%d]' refers to a " "field, '%s', not defined on %s.%s." % (cls.__name__, idx, field_name, model._meta.app_label, model.__name__)) if field_name not in cls.list_display: raise ImproperlyConfigured("'%s.list_editable[%d]' refers to " "'%s' which is not defined in 'list_display'." % (cls.__name__, idx, field_name)) if field_name in cls.list_display_links: raise ImproperlyConfigured("'%s' cannot be in both '%s.list_editable'" " and '%s.list_display_links'" % (field_name, cls.__name__, cls.__name__)) if not cls.list_display_links and cls.list_display[0] in cls.list_editable: raise ImproperlyConfigured("'%s.list_editable[%d]' refers to" " the first field in list_display, '%s', which can't be" " used unless list_display_links is set." % (cls.__name__, idx, cls.list_display[0])) if not field.editable: raise ImproperlyConfigured("'%s.list_editable[%d]' refers to a " "field, '%s', which isn't editable through the admin." % (cls.__name__, idx, field_name)) # search_fields = () if hasattr(cls, 'search_fields'): check_isseq(cls, 'search_fields', cls.search_fields) # date_hierarchy = None if cls.date_hierarchy: f = get_field(cls, model, opts, 'date_hierarchy', cls.date_hierarchy) if not isinstance(f, (models.DateField, models.DateTimeField)): raise ImproperlyConfigured("'%s.date_hierarchy is " "neither an instance of DateField nor DateTimeField." % cls.__name__) # ordering = None if cls.ordering: check_isseq(cls, 'ordering', cls.ordering) for idx, field in enumerate(cls.ordering): if field == '?' and len(cls.ordering) != 1: raise ImproperlyConfigured("'%s.ordering' has the random " "ordering marker '?', but contains other fields as " "well. Please either remove '?' or the other fields." % cls.__name__) if field == '?': continue if field.startswith('-'): field = field[1:] # Skip ordering in the format field1__field2 (FIXME: checking # this format would be nice, but it's a little fiddly). if '__' in field: continue get_field(cls, model, opts, 'ordering[%d]' % idx, field) if hasattr(cls, "readonly_fields"): check_readonly_fields(cls, model, opts) # list_select_related = False # save_as = False # save_on_top = False for attr in ('list_select_related', 'save_as', 'save_on_top'): if not isinstance(getattr(cls, attr), bool): raise ImproperlyConfigured("'%s.%s' should be a boolean." % (cls.__name__, attr)) # inlines = [] if hasattr(cls, 'inlines'): check_isseq(cls, 'inlines', cls.inlines) for idx, inline in enumerate(cls.inlines): if not issubclass(inline, BaseModelAdmin): raise ImproperlyConfigured("'%s.inlines[%d]' does not inherit " "from BaseModelAdmin." % (cls.__name__, idx)) if not inline.model: raise ImproperlyConfigured("'model' is a required attribute " "of '%s.inlines[%d]'." % (cls.__name__, idx)) if not issubclass(inline.model, models.Model): raise ImproperlyConfigured("'%s.inlines[%d].model' does not " "inherit from models.Model." % (cls.__name__, idx)) validate_base(inline, inline.model) validate_inline(inline, cls, model)
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 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. 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)
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): 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 validate(cls, model): """ Does basic ModelAdmin option validation. Calls custom validation classmethod in the end if it is provided in cls. The signature of the custom validation classmethod should be: def validate(cls, model). """ # Before we can introspect models, they need to be fully loaded so that # inter-relations are set up correctly. We force that here. models.get_apps() opts = model._meta validate_base(cls, model) # list_display if hasattr(cls, 'list_display'): check_isseq(cls, 'list_display', cls.list_display) for idx, field in enumerate(cls.list_display): if not callable(field): if not hasattr(cls, field): if not hasattr(model, field): try: opts.get_field(field) except models.FieldDoesNotExist: raise ImproperlyConfigured("%s.list_display[%d], %r is not a callable or an attribute of %r or found in the model %r." % (cls.__name__, idx, field, cls.__name__, model._meta.object_name)) else: # getattr(model, field) could be an X_RelatedObjectsDescriptor f = fetch_attr(cls, model, opts, "list_display[%d]" % idx, field) if isinstance(f, models.ManyToManyField): raise ImproperlyConfigured("'%s.list_display[%d]', '%s' is a ManyToManyField which is not supported." % (cls.__name__, idx, field)) # list_display_links if hasattr(cls, 'list_display_links'): check_isseq(cls, 'list_display_links', cls.list_display_links) for idx, field in enumerate(cls.list_display_links): if field not in cls.list_display: raise ImproperlyConfigured("'%s.list_display_links[%d]' " "refers to '%s' which is not defined in 'list_display'." % (cls.__name__, idx, field)) # list_filter if hasattr(cls, 'list_filter'): check_isseq(cls, 'list_filter', cls.list_filter) for idx, item in enumerate(cls.list_filter): # There are three options for specifying a filter: # 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 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: try: # Check for option #2 (tuple) field, list_filter_class = item except (TypeError, ValueError): # item is option #1 field = item else: # item is option #2 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__)) # 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)) # list_per_page = 100 if hasattr(cls, 'list_per_page') and not isinstance(cls.list_per_page, int): raise ImproperlyConfigured("'%s.list_per_page' should be a integer." % cls.__name__) # list_editable if hasattr(cls, 'list_editable') and cls.list_editable: check_isseq(cls, 'list_editable', cls.list_editable) for idx, field_name in enumerate(cls.list_editable): try: field = opts.get_field_by_name(field_name)[0] except models.FieldDoesNotExist: raise ImproperlyConfigured("'%s.list_editable[%d]' refers to a " "field, '%s', not defined on %s." % (cls.__name__, idx, field_name, model.__name__)) if field_name not in cls.list_display: raise ImproperlyConfigured("'%s.list_editable[%d]' refers to " "'%s' which is not defined in 'list_display'." % (cls.__name__, idx, field_name)) if field_name in cls.list_display_links: raise ImproperlyConfigured("'%s' cannot be in both '%s.list_editable'" " and '%s.list_display_links'" % (field_name, cls.__name__, cls.__name__)) if not cls.list_display_links and cls.list_display[0] in cls.list_editable: raise ImproperlyConfigured("'%s.list_editable[%d]' refers to" " the first field in list_display, '%s', which can't be" " used unless list_display_links is set." % (cls.__name__, idx, cls.list_display[0])) if not field.editable: raise ImproperlyConfigured("'%s.list_editable[%d]' refers to a " "field, '%s', which isn't editable through the admin." % (cls.__name__, idx, field_name)) # search_fields = () if hasattr(cls, 'search_fields'): check_isseq(cls, 'search_fields', cls.search_fields) # date_hierarchy = None if cls.date_hierarchy: f = get_field(cls, model, opts, 'date_hierarchy', cls.date_hierarchy) if not isinstance(f, (models.DateField, models.DateTimeField)): raise ImproperlyConfigured("'%s.date_hierarchy is " "neither an instance of DateField nor DateTimeField." % cls.__name__) # ordering = None if cls.ordering: check_isseq(cls, 'ordering', cls.ordering) for idx, field in enumerate(cls.ordering): if field == '?' and len(cls.ordering) != 1: raise ImproperlyConfigured("'%s.ordering' has the random " "ordering marker '?', but contains other fields as " "well. Please either remove '?' or the other fields." % cls.__name__) if field == '?': continue if field.startswith('-'): field = field[1:] # Skip ordering in the format field1__field2 (FIXME: checking # this format would be nice, but it's a little fiddly). if '__' in field: continue get_field(cls, model, opts, 'ordering[%d]' % idx, field) if hasattr(cls, "readonly_fields"): check_readonly_fields(cls, model, opts) # list_select_related = False # save_as = False # save_on_top = False for attr in ('list_select_related', 'save_as', 'save_on_top'): if not isinstance(getattr(cls, attr), bool): raise ImproperlyConfigured("'%s.%s' should be a boolean." % (cls.__name__, attr)) # inlines = [] if hasattr(cls, 'inlines'): check_isseq(cls, 'inlines', cls.inlines) for idx, inline in enumerate(cls.inlines): if not issubclass(inline, BaseModelAdmin): raise ImproperlyConfigured("'%s.inlines[%d]' does not inherit " "from BaseModelAdmin." % (cls.__name__, idx)) if not inline.model: raise ImproperlyConfigured("'model' is a required attribute " "of '%s.inlines[%d]'." % (cls.__name__, idx)) if not issubclass(inline.model, models.Model): raise ImproperlyConfigured("'%s.inlines[%d].model' does not " "inherit from models.Model." % (cls.__name__, idx)) validate_base(inline, inline.model) validate_inline(inline, cls, model)