def filter_queryset(self, request, queryset, view): try: types = None for key, value in request.query_params.items(): if key == 'type': if ',' in value: types = value.split(',') else: types = (value,) if types: types_map = {} for ct in ContentType.objects.filter(Q(app_label='main') | Q(app_label='auth', model='user')): ct_model = ct.model_class() if not ct_model: continue ct_type = get_type_for_model(ct_model) types_map[ct_type] = ct.pk model = queryset.model model_type = get_type_for_model(model) if 'polymorphic_ctype' in get_all_field_names(model): types_pks = set([v for k, v in types_map.items() if k in types]) queryset = queryset.filter(polymorphic_ctype_id__in=types_pks) elif model_type in types: queryset = queryset else: queryset = queryset.none() return queryset except FieldError as e: # Return a 400 for invalid field names. raise ParseError(*e.args)
def filter_queryset(self, request, queryset, view): try: order_by = None for key, value in request.query_params.items(): if key in ('order', 'order_by'): order_by = value if ',' in value: order_by = value.split(',') else: order_by = (value,) if order_by: order_by = self._strip_sensitive_model_fields(queryset.model, order_by) # Special handling of the type field for ordering. In this # case, we're not sorting exactly on the type field, but # given the limited number of views with multiple types, # sorting on polymorphic_ctype.model is effectively the same. new_order_by = [] if 'polymorphic_ctype' in get_all_field_names(queryset.model): for field in order_by: if field == 'type': new_order_by.append('polymorphic_ctype__model') elif field == '-type': new_order_by.append('-polymorphic_ctype__model') else: new_order_by.append(field) else: for field in order_by: if field not in ('type', '-type'): new_order_by.append(field) queryset = queryset.order_by(*new_order_by) return queryset except FieldError as e: # Return a 400 for invalid field names. raise ParseError(*e.args)
def get_description_context(self): if 'username' in get_all_field_names(self.model): order_field = 'username' else: order_field = 'name' d = super(ListAPIView, self).get_description_context() d.update({'order_field': order_field}) return d
def get_field_from_lookup(self, model, lookup): field = None parts = lookup.split('__') if parts and parts[-1] not in self.SUPPORTED_LOOKUPS: parts.append('exact') # FIXME: Could build up a list of models used across relationships, use # those lookups combined with request.user.get_queryset(Model) to make # sure user cannot query using objects he could not view. new_parts = [] # Store of all the fields used to detect repeats field_set = set([]) for name in parts[:-1]: # HACK: Make project and inventory source filtering by old field names work for backwards compatibility. if model._meta.object_name in ('Project', 'InventorySource'): name = { 'current_update': 'current_job', 'last_update': 'last_job', 'last_update_failed': 'last_job_failed', 'last_updated': 'last_job_run', }.get(name, name) if name == 'type' and 'polymorphic_ctype' in get_all_field_names(model): name = 'polymorphic_ctype' new_parts.append('polymorphic_ctype__model') else: new_parts.append(name) if name in getattr(model, 'PASSWORD_FIELDS', ()): raise PermissionDenied(_('Filtering on password fields is not allowed.')) elif name == 'pk': field = model._meta.pk else: name_alt = name.replace("_", "") if name_alt in model._meta.fields_map.keys(): field = model._meta.fields_map[name_alt] new_parts.pop() new_parts.append(name_alt) else: field = model._meta.get_field(name) if 'auth' in name or 'token' in name: raise PermissionDenied(_('Filtering on %s is not allowed.' % name)) if isinstance(field, ForeignObjectRel) and getattr(field.field, '__prevent_search__', False): raise PermissionDenied(_('Filtering on %s is not allowed.' % name)) elif getattr(field, '__prevent_search__', False): raise PermissionDenied(_('Filtering on %s is not allowed.' % name)) if field in field_set: # Field traversed twice, could create infinite JOINs, DoSing Tower raise ParseError(_('Loops not allowed in filters, detected on field {}.').format(field.name)) field_set.add(field) model = getattr(field, 'related_model', None) or field.model if parts: new_parts.append(parts[-1]) new_lookup = '__'.join(new_parts) return field, new_lookup
def get_fields_from_path(model, path): ''' Given a Django ORM lookup path (possibly over multiple models) Returns the fields in the line, and also the revised lookup path ex., given model=Organization path='project__timeout' returns tuple of fields traversed as well and a corrected path, for special cases we do substitutions ([<IntegerField for timeout>], 'project__timeout') ''' # Store of all the fields used to detect repeats field_list = [] new_parts = [] for name in path.split('__'): if model is None: raise ParseError(_('No related model for field {}.').format(name)) # HACK: Make project and inventory source filtering by old field names work for backwards compatibility. if model._meta.object_name in ('Project', 'InventorySource'): name = { 'current_update': 'current_job', 'last_update': 'last_job', 'last_update_failed': 'last_job_failed', 'last_updated': 'last_job_run', }.get(name, name) if name == 'type' and 'polymorphic_ctype' in get_all_field_names(model): name = 'polymorphic_ctype' new_parts.append('polymorphic_ctype__model') else: new_parts.append(name) if name in getattr(model, 'PASSWORD_FIELDS', ()): raise PermissionDenied(_('Filtering on password fields is not allowed.')) elif name == 'pk': field = model._meta.pk else: name_alt = name.replace("_", "") if name_alt in model._meta.fields_map.keys(): field = model._meta.fields_map[name_alt] new_parts.pop() new_parts.append(name_alt) else: field = model._meta.get_field(name) if isinstance(field, ForeignObjectRel) and getattr(field.field, '__prevent_search__', False): raise PermissionDenied(_('Filtering on %s is not allowed.' % name)) elif getattr(field, '__prevent_search__', False): raise PermissionDenied(_('Filtering on %s is not allowed.' % name)) if field in field_list: # Field traversed twice, could create infinite JOINs, DoSing Tower raise ParseError(_('Loops not allowed in filters, detected on field {}.').format(field.name)) field_list.append(field) model = getattr(field, 'related_model', None) return field_list, '__'.join(new_parts)