def _get_resource_filter(self, identifiers_prefix, identifiers, model, resource, request, filters_fields_rfs): """ :param identifiers_prefix: because filters are recursive if model relations property contains list of identifiers that was used for recursive searching the filter. :param identifiers: list of identifiers that conclusively identifies the filter. :param model: django model class. :param resource: resource object. :param request: django HTTP request. :param filters_fields_rfs: RFS of fields that is allowed to filter. :return: method returns filter that is obtained from resource and its methods. """ # Filter is obtained from resource filters dict for i in range(1, len(identifiers) + 1): # Because resource filters can contains filter key with __ we must try all combinations with suffixes current_identifiers = identifiers[:i] identifiers_string = LOOKUP_SEP.join(current_identifiers) identifiers_suffix = identifiers[i:] suffix_string = LOOKUP_SEP.join(identifiers_suffix) if (resource and identifiers_string in resource.filters and (not suffix_string or suffix_string in resource.filters[identifiers_string].get_suffixes())): return resource.filters[identifiers_string](identifiers_prefix, current_identifiers, identifiers_suffix) # Filter is obtained from resource methods current_identifier = identifiers[0] resource_method = get_method_or_none(resource, current_identifier) if current_identifier in filters_fields_rfs and resource_method: return self._get_method_filter(resource_method, identifiers_suffix, [current_identifier], identifiers[1:], model, resource, request, filters_fields_rfs) return None
def _get_resource_filter(self, identifiers_prefix, identifiers, model, resource, request, filters_fields_rfs): """ :param identifiers_prefix: because filters are recursive if model relations property contains list of identifiers that was used for recursive searching the filter. :param identifiers: list of identifiers that conclusively identifies the filter. :param model: django model class. :param resource: resource object. :param request: django HTTP request. :param filters_fields_rfs: RFS of fields that is allowed to filter. :return: method returns filter that is obtained from resource and its methods. """ # Filter is obtained from resource filters dict for i in range(1, len(identifiers) + 1): # Because resource filters can contains filter key with __ we must try all combinations with suffixes current_identifiers = identifiers[:i] identifiers_string = self._get_real_field_name(resource, LOOKUP_SEP.join(current_identifiers)) identifiers_suffix = identifiers[i:] suffix_string = LOOKUP_SEP.join(identifiers_suffix) if (resource and identifiers_string in resource.filters and (not suffix_string or suffix_string in resource.filters[identifiers_string].get_suffixes())): return resource.filters[identifiers_string]( identifiers_prefix, current_identifiers, identifiers_suffix, model ) # Filter is obtained from resource methods current_identifier = self._get_real_field_name(resource, identifiers[0]) resource_method = resource.get_method_returning_field_value(current_identifier) if resource else None if current_identifier in filters_fields_rfs and resource_method: return self._get_method_filter( resource_method, identifiers_suffix, [current_identifier], identifiers[1:], model, resource, request, filters_fields_rfs ) return None
def _get_sorter_from_resource(self, identifiers_prefix, identifiers, direction, model, resource, request, order_fields_rfs): """ :param identifiers_prefix: because order strings are recursive if model relations property contains list of identifiers that was used for recursive searching the order string. :param identifiers: list of identifiers that conclusively identifies order string. :param direction: direction of ordering ASC or DESC. :param model: django model class. :param resource: resource object. :param request: django HTTP request. :param order_fields_rfs: RFS of fields that is allowed to order. :return: db order method string that is obtained from resource object. """ full_identifiers_string = self._get_real_field_name( resource, LOOKUP_SEP.join(identifiers)) resource_method = resource.get_method_returning_field_value( full_identifiers_string) if resource else None if full_identifiers_string in order_fields_rfs and resource_method: return self._get_sorter_from_method(resource_method, identifiers_prefix, identifiers, direction, model, resource, request, order_fields_rfs)
def _get_model_filter(self, identifiers_prefix, identifiers, model, resource, request, filters_fields_rfs): """ :param identifiers_prefix: because filters are recursive if model relations property contains list of identifiers that was used for recursive searching the filter. :param identifiers: list of identifiers that conclusively identifies the filter. :param model: django model class. :param resource: resource object. :param request: django HTTP request. :param filters_fields_rfs: RFS of fields that is allowed to filter. :return: method returns filter from model fields or methods """ current_identifier = identifiers[0] identifiers_suffix = identifiers[1:] if current_identifier not in filters_fields_rfs: raise FilterIdentifierError suffix = LOOKUP_SEP.join(identifiers_suffix) model_field = get_field_or_none(model, current_identifier) model_method = get_method_or_none(model, current_identifier) if model_field and model_field.filter and (not suffix or suffix in model_field.filter.get_suffixes()): return model_field.filter(identifiers_prefix, [current_identifier], identifiers_suffix, field=model_field) elif model_field and model_field.is_relation and model_field.related_model: # recursive search for filter via related model fields next_model = model_field.related_model next_resource = get_resource_or_none(request, next_model, getattr(resource, 'resource_typemapper')) return self._get_filter_recursive( identifiers_prefix + [identifiers[0]], identifiers[1:], next_model, next_resource, request, filters_fields_rfs[current_identifier].subfieldset ) elif model_method: return self._get_method_filter(model_method, identifiers_prefix, [current_identifier], identifiers_suffix, model, resource, request, filters_fields_rfs)
def _get_method_filter(self, method, identifiers_prefix, identifiers, identifiers_suffix, model, resource, request, filters_fields_rfs): """ :param method: method from which we can get filter. :param identifiers_prefix: because filters are recursive if model relations property contains list of identifiers that was used for recursive searching the filter. :param identifiers: list of identifiers that conclusively identifies the filter. :param identifiers_suffix: list of suffixes that can be used for more specific filters. :param model: django model class. :param resource: resource object. :param request: django HTTP request. :param filters_fields_rfs: RFS of fields that is allowed to filter. :return: method returns filter object that is obtained from method. """ if hasattr(method, 'filter_by'): # If method has filter_by attribute filter is being searched according to this value. filter_by_identifiers = method.filter_by.split(LOOKUP_SEP) next_identifiers = filter_by_identifiers + identifiers_suffix # Because method must be inside allowed filter fields RFS, we must add value filter_by of the method # to the next RFS. next_filters_fields_rfs = rfs(filter_by_identifiers) return self._get_filter_recursive( identifiers_prefix, next_identifiers, model, resource, request, next_filters_fields_rfs ) suffix = LOOKUP_SEP.join(identifiers_suffix) if not hasattr(method, 'filter') or (suffix and suffix not in method.filter.get_suffixes()): raise FilterIdentifierError return method.filter(identifiers_prefix, identifiers, identifiers_suffix, method=method)
def _get_method_filter(self, method, identifiers_prefix, identifiers, identifiers_suffix, model, resource, request, filters_fields_rfs): """ :param method: method from which we can get filter. :param identifiers_prefix: because filters are recursive if model relations property contains list of identifiers that was used for recursive searching the filter. :param identifiers: list of identifiers that conclusively identifies the filter. :param identifiers_suffix: list of suffixes that can be used for more specific filters. :param model: django model class. :param resource: resource object. :param request: django HTTP request. :param filters_fields_rfs: RFS of fields that is allowed to filter. :return: method returns filter object that is obtained from method. """ if hasattr(method, 'filter_by'): # If method has filter_by attribute filter is being searched according to this value. filter_by_identifiers = method.filter_by.split(LOOKUP_SEP) next_identifiers = filter_by_identifiers + identifiers_suffix # Because method must be inside allowed filter fields RFS, we must add value filter_by of the method # to the next RFS. next_filters_fields_rfs = rfs(filter_by_identifiers) return self._get_filter_recursive(identifiers_prefix, next_identifiers, model, resource, request, next_filters_fields_rfs) suffix = LOOKUP_SEP.join(identifiers_suffix) if not hasattr(method, 'filter') or ( suffix and suffix not in method.filter.get_suffixes()): raise FilterIdentifierError return method.filter(identifiers_prefix, identifiers, identifiers_suffix, method=method)
def _get_order_string_from_model(self, identifiers_prefix, identifiers, model, resource, request, order_fields_rfs): """ :param identifiers_prefix: because order strings are recursive if model relations property contains list of identifiers that was used for recursive searching the order string. :param identifiers: list of identifiers that conclusively identifies order string. :param model: django model class. :param resource: resource object. :param request: django HTTP request. :param order_fields_rfs: RFS of fields that is allowed to order. :return: db order method string that is obtained from model fields or methods. """ current_identifier = identifiers[0] identifiers_suffix = identifiers[1:] if current_identifier not in order_fields_rfs: raise OrderIdentifierError model_field = get_field_or_none(model, current_identifier) model_method = get_method_or_none(model, current_identifier) if model_field and not identifiers_suffix and (not model_field.is_relation or model_field.related_model): return LOOKUP_SEP.join(identifiers_prefix + identifiers) elif model_field and model_field.is_relation and model_field.related_model: next_model = model_field.related_model next_resource = get_resource_or_none(request, next_model, getattr(resource, 'resource_typemapper')) return self._get_order_string_recursive( identifiers_prefix + [identifiers[0]], identifiers[1:], next_model, next_resource, request, order_fields_rfs[current_identifier].subfieldset ) elif model_method and not identifiers_suffix: return self._get_order_string_from_method( model_method, identifiers_prefix, identifiers, model, resource, request, order_fields_rfs )
def get_q(self, filter, value, operator_slug, request): filter_term = {} value = filter.clean_value(value, operator_slug, request) for attr in filter.get_suffixes(): date_val = getattr(value, attr) if date_val: filter_term[LOOKUP_SEP.join((filter.get_full_filter_key(), attr))] = date_val return Q(**filter_term)
def get_q(self, filter, value, operator_slug, request): filter_term = {} value = filter.clean_value(value, operator_slug, request) for attr in filter.get_suffixes(): date_val = getattr(value, attr) if date_val: filter_term[LOOKUP_SEP.join( (filter.get_full_filter_key(), attr))] = date_val return Q(**filter_term)
def _update_q_with_prefix(self, q): """ Because implementation of custom filter should be as simple as possible this methods add identifier prefixes to the Q objects. """ if isinstance(q, Q): q.children = [self._update_q_with_prefix(child) for child in q.children] return q else: return ( LOOKUP_SEP.join((filter_part for filter_part in self.identifiers_prefix + [q[0]] if filter_part)), q[1] )
def _add(self, key_path): if not self._tree_contains(key_path): current = self.root for key in key_path: current[key] = current.get(key, {}) prev = current current = current[key] if current: self._remove_childs(key_path, current) prev[key] = {} self.fieldset[LOOKUP_SEP.join(key_path)] = None
def _update_q_with_prefix(self, q): """ Because implementation of custom filter should be as simple as possible this methods add identifier prefixes to the Q objects. """ if isinstance(q, Q): q.children = [ self._update_q_with_prefix(child) for child in q.children ] return q else: return (LOOKUP_SEP.join( (filter_part for filter_part in self.identifiers_prefix + [q[0]] if filter_part)), q[1])
def get_q(self, filter, values, operator_slug, request): if not isinstance(values, list): raise FilterValueError(ugettext('Value must be list')) else: values = self._clean_list_values(filter, operator_slug, request, values) qs_obj_with_all_values = filter.field.model.objects.all() for v in set(values): qs_obj_with_all_values = qs_obj_with_all_values.filter(**{filter.identifiers[-1]: v}) return Q( **{ '{}__in'.format( LOOKUP_SEP.join(filter.identifiers[:-1] + ['pk']) ): qs_obj_with_all_values.values('pk') } )
def _get_order_string_from_resource(self, identifiers_prefix, identifiers, model, resource, request, order_fields_rfs): """ :param identifiers_prefix: because order strings are recursive if model relations property contains list of identifiers that was used for recursive searching the order string. :param identifiers: list of identifiers that conclusively identifies order string. :param model: django model class. :param resource: resource object. :param request: django HTTP request. :param order_fields_rfs: RFS of fields that is allowed to order. :return: db order method string that is obtained from resource object. """ full_identifiers_string = LOOKUP_SEP.join(identifiers) resource_method = get_method_or_none(resource, full_identifiers_string) if full_identifiers_string in order_fields_rfs and resource_method: return self._get_order_string_from_method(resource_method, identifiers_prefix, identifiers, model, resource, request, order_fields_rfs)
def get_q(self, filter, values, operator_slug, request): if not isinstance(values, list): raise FilterValueError(ugettext('Value must be list')) else: values = self._clean_list_values(filter, operator_slug, request, values) qs_obj_with_all_values = filter.field.model.objects.all() for v in set(values): qs_obj_with_all_values = qs_obj_with_all_values.filter( **{filter.identifiers[-1]: v}) return Q( **{ '{}__in'.format( LOOKUP_SEP.join(filter.identifiers[:-1] + ['pk'])): qs_obj_with_all_values.values('pk') })
def _get_model_filter(self, identifiers_prefix, identifiers, model, resource, request, filters_fields_rfs): """ :param identifiers_prefix: because filters are recursive if model relations property contains list of identifiers that was used for recursive searching the filter. :param identifiers: list of identifiers that conclusively identifies the filter. :param model: django model class. :param resource: resource object. :param request: django HTTP request. :param filters_fields_rfs: RFS of fields that is allowed to filter. :return: method returns filter from model fields or methods """ current_identifier = identifiers[0] identifiers_suffix = identifiers[1:] if current_identifier not in filters_fields_rfs: raise FilterIdentifierError suffix = LOOKUP_SEP.join(identifiers_suffix) model_field = get_field_or_none(model, current_identifier) model_method = get_method_or_none(model, current_identifier) if model_field and model_field.filter and ( not suffix or suffix in model_field.filter.get_suffixes()): return model_field.filter(identifiers_prefix, [current_identifier], identifiers_suffix, field=model_field) elif model_field and model_field.is_relation and model_field.related_model: # recursive search for filter via related model fields next_model = model_field.related_model next_resource = get_resource_or_none( request, next_model, getattr(resource, 'resource_typemapper')) return self._get_filter_recursive( identifiers_prefix + [identifiers[0]], identifiers[1:], next_model, next_resource, request, filters_fields_rfs[current_identifier].subfieldset) elif model_method: return self._get_method_filter(model_method, identifiers_prefix, [current_identifier], identifiers_suffix, model, resource, request, filters_fields_rfs)
def _recursive_generator(self, fields, fields_string, model=None, key_path=None, label_path=None, extended_fieldset=None): key_path = key_path or [] label_path = label_path or [] allowed_fieldset = self._get_allowed_fieldset() if extended_fieldset: allowed_fieldset.join(extended_fieldset) parsed_fields = [ (field_name, subfields_string) for field_name, subfields_string in self._parse_fields_string(fields_string) if field_name in allowed_fieldset or self.direct_serialization ] for field_name, subfields_string in parsed_fields: self._recursive_generator( fields, subfields_string, get_model_from_relation_or_none(model, field_name) if model else None, key_path + [field_name], label_path + [self._get_label(field_name, model)], extended_fieldset=allowed_fieldset[field_name].subfieldset if allowed_fieldset[field_name] else None ) if not parsed_fields: label = self.resource.get_field_labels().get(LOOKUP_SEP.join(key_path)) if self.resource else None fields.append(Field(key_path, [label] if label else label_path))
def _remove_childs(self, key_path, tree): if not tree: del self.fieldset[LOOKUP_SEP.join(key_path)] else: for key, subtree in tree.items(): self._remove_childs(key_path + [key], subtree)
def get_full_filter_key(self): return LOOKUP_SEP.join(self.full_identifiers)
def _get_order_string(self): return LOOKUP_SEP.join(self.identifiers)
def _get_order_string(self): return LOOKUP_SEP.join(chain(('extra_order', ), self.identifiers))
def __hash__(self): return hash(LOOKUP_SEP.join(self.key_path))
def _get_order_string(self): return LOOKUP_SEP.join(chain(('extra_order',), self.identifiers))