def QT(field_lookup, value): lookup_pos = -1 for lookup in QUERY_TERMS.keys(): ltemp = field_lookup.rfind(lookup) if ltemp != -1: lookup_pos = ltemp break # we have a lookup if lookup_pos != -1: field_ = field_lookup[:lookup_pos-3] lookup = field_lookup[lookup_pos-3:] else: field_ = field_lookup lookup = '' field_pos = field_.rfind('__') # we don't have a relationship if field_pos == -1: field = field_ else: field = field_[field_.rfind('__')+2:] relationship = field_[:field_.rfind(field)] field = format_field_name(field) q = relationship + field + lookup kwargs = {str(q): value} return Q(**kwargs)
def build_filters(self, filters=None): """ Given a dictionary of filters, create the necessary ORM-level filters. Keys should be resource fields, **NOT** model fields. Valid values are either a list of Django filter types (i.e. ``['startswith', 'exact', 'lte']``), the ``ALL`` constant or the ``ALL_WITH_RELATIONS`` constant. """ # At the declarative level: # filtering = { # 'resource_field_name': ['exact', 'gt', 'gte', 'lt', 'lte', 'range'], # 'resource_field_name_3': ALL, # 'resource_field_name_4': ALL_WITH_RELATIONS, # ... # } # Accepts the filters as a dict. None by default, meaning no filters. if filters is None: filters = {} qs_filters = {} for filter_expr, value in filters.items(): filter_bits = filter_expr.split(LOOKUP_SEP) field_name = filter_bits.pop(0) filter_type = 'exact' if not field_name in self.fields: # It's not a field we know about. Move along citizen. continue if len(filter_bits) and filter_bits[-1] in QUERY_TERMS.keys(): filter_type = filter_bits.pop() lookup_bits = self.check_filtering(field_name, filter_type, filter_bits) if value in ['true', 'True', True]: value = True elif value in ['false', 'False', False]: value = False elif value in ('nil', 'none', 'None', None): value = None # Split on ',' if not empty string and either an in or range filter. if filter_type in ('in', 'range') and len(value): if hasattr(filters, 'getlist'): value = filters.getlist(filter_expr) else: value = value.split(',') redis_model_field_name = LOOKUP_SEP.join(lookup_bits) qs_filter = "%s%s%s" % (redis_model_field_name, LOOKUP_SEP, filter_type) qs_filters[qs_filter] = value return dict_strip_unicode_keys(qs_filters)
def build_filters(self, filters=None): """ Given a dictionary of filters, create the necessary ORM-level filters. Keys should be resource fields, **NOT** model fields. Valid values are either a list of Django filter types (i.e. ``['startswith', 'exact', 'lte']``), the ``ALL`` constant or the ``ALL_WITH_RELATIONS`` constant. """ # At the declarative level: # filtering = { # 'resource_field_name': ['exact', 'startswith', 'endswith', 'contains'], # 'resource_field_name_2': ['exact', 'gt', 'gte', 'lt', 'lte', 'range'], # 'resource_field_name_3': ALL, # 'resource_field_name_4': ALL_WITH_RELATIONS, # ... # } # Accepts the filters as a dict. None by default, meaning no filters. if filters is None: filters = {} qs_filters = {} for filter_expr, value in filters.items(): filter_bits = filter_expr.split(LOOKUP_SEP) field_name = filter_bits.pop(0) filter_type = 'exact' if not field_name in self.fields: # It's not a field we know about. Move along citizen. continue if len(filter_bits) and filter_bits[-1] in QUERY_TERMS.keys(): filter_type = filter_bits.pop() lookup_bits = self.check_filtering(field_name, filter_type, filter_bits) if value in ['true', 'True', True]: value = True elif value in ['false', 'False', False]: value = False elif value in ('nil', 'none', 'None', None): value = None # Split on ',' if not empty string and either an in or range filter. if filter_type in ('in', 'range') and len(value): if hasattr(filters, 'getlist'): value = filters.getlist(filter_expr) else: value = value.split(',') db_field_name = LOOKUP_SEP.join(lookup_bits) qs_filter = "%s%s%s" % (db_field_name, LOOKUP_SEP, filter_type) qs_filters[qs_filter] = value return dict_strip_unicode_keys(qs_filters)
def build_filters(self, filters=None): if filters is None: filters = {} qs_filters = {} for filter_expr, value in filters.items(): filter_bits = filter_expr.split(LOOKUP_SEP) field_name = filter_bits.pop(0) filter_type = 'exact' if not field_name in self.fields: # It's not a field we know about. Move along citizen. continue if len(filter_bits) and filter_bits[-1] in QUERY_TERMS.keys(): filter_type = filter_bits.pop() lookup_bits = self.check_filtering(field_name, filter_type, filter_bits) # Is the field defined in our resource? field = getattr(self, field_name) # If we have a field defined use its type conversion if field: value = field.convert(value) elif isinstance(value, basestring): if value.lower() in ('true', 'yes'): value = True elif value.lower() in ('false', 'no'): value = False elif value.lower() in ('nil', 'none', 'null'): value = None # elif value.isnumeric(): # value = int(value) # Split on ',' if not empty string and either an in or range filter. if filter_type in ('in', 'range') and len(value): if hasattr(filters, 'getlist'): value = filters.getlist(filter_expr) else: value = value.split(',') qs_filters[field_name] = { 'filter_type': filter_type, 'value': value } return dict_strip_unicode_keys(qs_filters)
def build_filters(self, filters=None): if not filters: return { } # Replace enumeration keys with the right values. from django.db.models.sql.constants import QUERY_TERMS, LOOKUP_SEP f = { } for k, v in filters.items(): path = k.split(LOOKUP_SEP) if len(path) and path[-1] in QUERY_TERMS.keys(): path.pop() model, field = self.find_field(path) if model: enum = model.Meta.queryset.model._meta.get_field(field).choices if GBaseModel.is_enum(enum): v = int(enum.by_key(v)) f[k] = v return super(GBaseModel, self).build_filters(filters=f)
def build_filters(self, filters=None): # Override the filters so we can stop Tastypie silently ignoring # invalid filters. That will cause an invalid filtering just to return # lots of results. if filters is None: filters = {} qs_filters = {} for filter_expr, value in filters.items(): filter_bits = filter_expr.split(LOOKUP_SEP) field_name = filter_bits.pop(0) filter_type = 'exact' if not field_name in self.fields: # Don't just ignore this. Tell the world. Shame I have to # override all this, just to do this. raise InvalidFilterError('Not a valid filtering field: %s' % field_name) if len(filter_bits) and filter_bits[-1] in QUERY_TERMS.keys(): filter_type = filter_bits.pop() lookup_bits = self.check_filtering(field_name, filter_type, filter_bits) if value in ['true', 'True', True]: value = True elif value in ['false', 'False', False]: value = False elif value in ('nil', 'none', 'None', None): value = None # Split on ',' if not empty string and either an in or range # filter. if filter_type in ('in', 'range') and len(value): if hasattr(filters, 'getlist'): value = filters.getlist(filter_expr) else: value = value.split(',') db_field_name = LOOKUP_SEP.join(lookup_bits) qs_filter = "%s%s%s" % (db_field_name, LOOKUP_SEP, filter_type) qs_filters[qs_filter] = value return dict_strip_unicode_keys(qs_filters)
def filters_for_model(model, fields=None, exclude=None, filter_for_field=None): field_dict = SortedDict() opts = model._meta if fields is None: fields = [f.name for f in sorted(opts.fields + opts.many_to_many)] for f in fields: if exclude is not None and f in exclude: continue if f.split(LOOKUP_SEP)[-1] in QUERY_TERMS.keys(): lookup_type = f.split(LOOKUP_SEP)[-1] f = LOOKUP_SEP.join(f.split(LOOKUP_SEP)[:-1]) else: lookup_type = None field = get_model_field(model, f) if field is None: field_dict[f] = None continue filter_ = filter_for_field(field, f, lookup_type) if filter_: field_dict[f] = filter_ return field_dict
def build_parameters_from_filters(self, prefix="", method='GET'): parameters = [] # Deal with the navigational filters. # Always add the limits & offset params on the root ( aka not prefixed ) object. if not prefix and method.upper() == 'GET': navigation_filters = [ ('limit','int','Specify the number of element to display per page.'), ('offset','int','Specify the offset to start displaying element on a page.'), ] for name, type, desc in navigation_filters: parameters.append(self.build_parameter( paramType="query", name=name, dataType=type, required=False, description=force_unicode(desc), )) # For non-standard API functionality, allow the User to declaritively # define their own filters, along with Swagger endpoint values. # Minimal error checking here. If the User understands enough to want to # do this, assume that they know what they're doing. if hasattr(self.resource.Meta, 'custom_filtering'): for name, field in self.resource.Meta.custom_filtering.items(): parameters.append(self.build_parameter( paramType = 'query', name = name, dataType = field['dataType'], required = field['required'], description = unicode(field['description']) )) return parameters if 'filtering' in self.schema and method.upper() == 'GET': for name, field in self.schema['filtering'].items(): # Integer value means this points to a related model if field in [ALL, ALL_WITH_RELATIONS]: if field == ALL: #TODO: Show all possible ORM filters for this field #This code has been mostly sucked from the tastypie lib if getattr(self.resource._meta, 'queryset', None) is not None: # Get the possible query terms from the current QuerySet. if hasattr(self.resource._meta.queryset.query.query_terms, 'keys'): # Django 1.4 & below compatibility. field = self.resource._meta.queryset.query.query_terms.keys() else: # Django 1.5+. field = self.resource._meta.queryset.query.query_terms else: if hasattr(QUERY_TERMS, 'keys'): # Django 1.4 & below compatibility. field = QUERY_TERMS.keys() else: # Django 1.5+. field = QUERY_TERMS elif field == ALL_WITH_RELATIONS: # Show all params from related model # Add a subset of filter only foreign-key compatible on the relation itself. # We assume foreign keys are only int based. field = ['gt','in','gte', 'lt', 'lte','exact'] # TODO This could be extended by checking the actual type of the relational field, but afaik it's also an issue on tastypie. related_resource = self.resource.fields[name].get_related_resource(None) related_mapping = ResourceSwaggerMapping(related_resource) parameters.extend(related_mapping.build_parameters_from_filters(prefix="%s%s__" % (prefix, name))) if isinstance( field, list ): # Skip if this is an incorrect filter if name not in self.schema['fields']: continue schema_field = self.schema['fields'][name] for query in field: if query == 'exact': description = force_unicode(schema_field['help_text']) dataType = schema_field['type'] # Use a better description for related models with exact filter if dataType == 'related': # Assume that related pk is an integer # TODO if youre not using integer ID for pk then we need to look this up somehow dataType = 'integer' description = 'ID of related resource' parameters.append(self.build_parameter( paramType="query", name="%s%s" % (prefix, name), dataType=dataType, required= False, description=description, )) else: parameters.append(self.build_parameter( paramType="query", name="%s%s__%s" % (prefix, name, query), dataType=schema_field['type'], required= False, description=force_unicode(schema_field['help_text']), )) return parameters
from django import forms from django.db.models import Q from django.db.models.sql.constants import QUERY_TERMS from django.utils.translation import ugettext_lazy as _ from django_filters.fields import RangeField, LookupTypeField, RealDateRangeField __all__ = [ 'Filter', 'CharFilter', 'BooleanFilter', 'ChoiceFilter', 'MultipleChoiceFilter', 'DateFilter', 'DateTimeFilter', 'TimeFilter', 'ModelChoiceFilter', 'ModelMultipleChoiceFilter', 'NumberFilter', 'RangeFilter', 'DateRangeFilter', 'AllValuesFilter', 'RealDateRangeFilter', 'VisitRangeFilter', 'GroupFilter', ] LOOKUP_TYPES = sorted(QUERY_TERMS.keys()) class Filter(object): creation_counter = 0 field_class = forms.Field def __init__(self, name=None, label=None, widget=None, action=None, lookup_type='exact', required=False, **kwargs): self.name = name self.label = label if action: self.filter = action self.lookup_type = lookup_type self.widget = widget self.required = required self.extra = kwargs
def build_parameters_from_filters(self, prefix="", method='GET'): parameters = [] # Deal with the navigational filters. # Always add the limits & offset params on the root ( aka not prefixed ) object. if not prefix and method.upper() == 'GET': navigation_filters = [ ('limit','int','Specify the number of element to display per page.'), ('offset','int','Specify the offset to start displaying element on a page.'), ] for name, type, desc in navigation_filters: parameters.append(self.build_parameter( paramType="query", name=name, dataType=type, required=False, description=force_unicode(desc), )) if 'filtering' in self.schema and method.upper() == 'GET': for name, field in self.schema['filtering'].items(): # Avoid infinite recursion for self referencing resource (issue #22) if not prefix.find('{}__'.format(name)) >= 0: # Integer value means this points to a related model if field in [ALL, ALL_WITH_RELATIONS]: if field == ALL: #This code has been mostly sucked from the tastypie lib if getattr(self.resource._meta, 'queryset', None) is not None: # Get the possible query terms from the current QuerySet. if hasattr(self.resource._meta.queryset.query.query_terms, 'keys'): # Django 1.4 & below compatibility. field = self.resource._meta.queryset.query.query_terms.keys() else: # Django 1.5+. field = self.resource._meta.queryset.query.query_terms else: if hasattr(QUERY_TERMS, 'keys'): # Django 1.4 & below compatibility. field = QUERY_TERMS.keys() else: # Django 1.5+. field = QUERY_TERMS elif field == ALL_WITH_RELATIONS: # Show all params from related model # Add a subset of filter only foreign-key compatible on the relation itself. # We assume foreign keys are only int based. field = ['gt','in','gte', 'lt', 'lte','exact'] # TODO This could be extended by checking the actual type of the relational field, but afaik it's also an issue on tastypie. related_resource = self.resource.fields[name].get_related_resource(None) related_mapping = ResourceSwaggerMapping(related_resource) parameters.extend(related_mapping.build_parameters_from_filters(prefix="%s%s__" % (prefix, name))) if isinstance(field, (list, tuple, set)): # Skip if this is an incorrect filter if name not in self.schema['fields']: continue schema_field = self.schema['fields'][name] dataType = schema_field['type'] if dataType == 'related': dataType = self.get_related_field_type(name) description = 'ID of related resource' for query in field: if query == 'exact': description = force_unicode(schema_field['help_text']) # Use a better description for related models with exact filter parameters.append(self.build_parameter( paramType="query", name="%s%s" % (prefix, name), dataType=dataType, required= False, description=description, )) else: parameters.append(self.build_parameter( paramType="query", name="%s%s__%s" % (prefix, name, query), dataType=dataType, required= False, description=force_unicode(schema_field['help_text']), )) return parameters
def build_parameters_from_filters(self, prefix="", method='GET'): parameters = [] # Deal with the navigational filters. # Always add the limits & offset params on the root ( aka not prefixed ) object. if not prefix and method.upper() == 'GET': navigation_filters = [ ('limit', 'int', 'Specify the number of element to display per page.'), ('offset', 'int', 'Specify the offset to start displaying element on a page.'), ] for name, type, desc in navigation_filters: parameters.append( self.build_parameter( paramType="query", name=name, dataType=type, required=False, description=force_text(desc), )) if 'filtering' in self.schema and method.upper() == 'GET': for name, field in self.schema['filtering'].items(): # Avoid infinite recursion for self referencing resource (issue #22) if not prefix.find('{0}__'.format(name)) >= 0: # Integer value means this points to a related model if field in [ALL, ALL_WITH_RELATIONS]: # For fields marked as ALL_WITH_RELATIONS, we must fetch information on their related resources as well. # However, tastypie allows us to mark fields that do not have related resources as ALL_WITH_RELATIONS. # This functions like a white list. # Therefore, we need to check whether a field actually has a related resource. if field == ALL: has_related_resource = False else: has_related_resource = hasattr( self.resource.fields[name], 'get_related_resource') if not has_related_resource: #This code has been mostly sucked from the tastypie lib if getattr(self.resource._meta, 'queryset', None) is not None: # Get the possible query terms from the current QuerySet. if hasattr( self.resource._meta.queryset.query. query_terms, 'keys'): # Django 1.4 & below compatibility. field = self.resource._meta.queryset.query.query_terms.keys( ) else: # Django 1.5+. field = self.resource._meta.queryset.query.query_terms else: if hasattr(QUERY_TERMS, 'keys'): # Django 1.4 & below compatibility. field = QUERY_TERMS.keys() else: # Django 1.5+. field = QUERY_TERMS else: # Show all params from related model # Add a subset of filter only foreign-key compatible on the relation itself. # We assume foreign keys are only int based. field = [ 'gt', 'in', 'gte', 'lt', 'lte', 'exact' ] # TODO This could be extended by checking the actual type of the relational field, but afaik it's also an issue on tastypie. related_resource = self.resource.fields[ name].get_related_resource(None) related_mapping = ResourceSwaggerMapping( related_resource) parameters.extend( related_mapping.build_parameters_from_filters( prefix="%s%s__" % (prefix, name))) if isinstance(field, (list, tuple, set)): # Skip if this is an incorrect filter if name not in self.schema['fields']: continue schema_field = self.schema['fields'][name] dataType = schema_field['type'] if dataType == 'related': dataType = self.get_related_field_type(name) description = 'ID of related resource' for query in field: if query == 'exact': description = force_text( schema_field['help_text']) # Use a better description for related models with exact filter parameters.append( self.build_parameter( paramType="query", name="%s%s" % (prefix, name), dataType=dataType, required=False, description=description, )) else: parameters.append( self.build_parameter( paramType="query", name="%s%s__%s" % (prefix, name, query), dataType=dataType, required=False, description=force_text( schema_field['help_text']), )) return parameters
def build_filters(self, filters=None): """ Given a dictionary of filters, create the necessary ORM-level filters. Keys should be resource fields, **NOT** model fields. Valid values are either a list of Django filter types (i.e. ``['startswith', 'exact', 'lte']``), the ``ALL`` constant or the ``ALL_WITH_RELATIONS`` constant. """ # At the declarative level: # filtering = { # 'resource_field_name': ['exact', 'startswith', 'endswith', 'contains'], # 'resource_field_name_2': ['exact', 'gt', 'gte', 'lt', 'lte', 'range'], # 'resource_field_name_3': ALL, # 'resource_field_name_4': ALL_WITH_RELATIONS, # ... # } # Accepts the filters as a dict. None by default, meaning no filters. if filters is None: filters = {} qs_filters = {} for filter_expr, value in filters.items(): filter_bits = filter_expr.split(LOOKUP_SEP) if not filter_bits[0] in self.fields: # It's not a field we know about. Move along citizen. continue if not filter_bits[0] in self._meta.filtering: raise InvalidFilterError("The '%s' field does not allow filtering." % filter_bits[0]) if filter_bits[-1] in QUERY_TERMS.keys(): filter_type = filter_bits.pop() else: filter_type = 'exact' # Check to see if it's allowed lookup type. if not self._meta.filtering[filter_bits[0]] in (ALL, ALL_WITH_RELATIONS): # Must be an explicit whitelist. if not filter_type in self._meta.filtering[filter_bits[0]]: raise InvalidFilterError("'%s' is not an allowed filter on the '%s' field." % (filter_expr, filter_bits[0])) # Check to see if it's a relational lookup and if that's allowed. if len(filter_bits) > 1: if not self._meta.filtering[filter_bits[0]] == ALL_WITH_RELATIONS: raise InvalidFilterError("Lookups are not allowed more than one level deep on the '%s' field." % filter_bits[0]) if self.fields[filter_bits[0]].attribute is None: raise InvalidFilterError("The '%s' field has no 'attribute' for searching with." % filter_bits[0]) if value == 'true': value = True elif value == 'false': value = False elif value in ('nil', 'none', 'None'): value = None db_field_name = LOOKUP_SEP.join([self.fields[filter_bits[0]].attribute] + filter_bits[1:]) qs_filter = "%s%s%s" % (db_field_name, LOOKUP_SEP, filter_type) qs_filters[qs_filter] = value return dict_strip_unicode_keys(qs_filters)
def build_filters(self, filters=None): """ An enhanced tastypie method that will first check to see if a value passed inside of a filter can be decoded as JSON. If so, it passes those values to ``::meth::<get_query_bits_from_dict> get_query_bits_from_dict``. """ if filters is None: filters = {} qs_filters = {} if hasattr(self._meta, 'queryset'): # Get the possible query terms from the current QuerySet. if hasattr(self._meta.queryset.query.query_terms, 'keys'): # Django 1.4 & below compatibility. query_terms = self._meta.queryset.query.query_terms.keys() else: # Django 1.5+. query_terms = self._meta.queryset.query.query_terms else: query_terms = QUERY_TERMS.keys() for filter_expr, value in filters.items(): filter_type = 'exact' # Check first to see if a value can be decoded as json try: value_dict = json.loads(value) # Because the python json module is not as strict as JSON standards # We must check to make sure that the value loaded is a dict if not isinstance(value_dict, dict): value_dict = None except ValueError: value_dict = None # If a value is decoded, it passes that dict into a special method if value_dict: # Get the value and filter_bits from the method. filter_bits, value = self.get_query_bits_from_dict(value_dict, keys_list=[], value=None) # Because the field_name and the filter_expr are backward, # we need to set the field name = to filter expr field_name = filter_expr # If not, stick with standard tastypie stuff else: filter_bits = filter_expr.split(LOOKUP_SEP) field_name = filter_bits.pop(0) if not field_name in self.fields: # It's not a field we know about. Move along citizen. continue if len(filter_bits) and filter_bits[-1] in query_terms: filter_type = filter_bits.pop() lookup_bits = self.check_filtering(field_name, filter_type, filter_bits) value = self.filter_value_to_python(value, field_name, filters, filter_expr, filter_type) db_field_name = LOOKUP_SEP.join(lookup_bits) qs_filter = "%s%s%s" % (db_field_name, LOOKUP_SEP, filter_type) qs_filters[qs_filter] = value return dict_strip_unicode_keys(qs_filters)
def build_parameters_from_filters(self, prefix="", method="GET"): parameters = [] # Deal with the navigational filters. # Always add the limits & offset params on the root ( aka not prefixed ) object. if not prefix and method.upper() == "GET": navigation_filters = [ ("limit", "int", "Specify the number of element to display per page."), ("offset", "int", "Specify the offset to start displaying element on a page."), ] for name, type, desc in navigation_filters: parameters.append( self.build_parameter( paramType="query", name=name, dataType=type, required=False, description=force_unicode(desc) ) ) if "filtering" in self.schema and method.upper() == "GET": for name, field in self.schema["filtering"].items(): # Integer value means this points to a related model if field in [ALL, ALL_WITH_RELATIONS]: if field == ALL: # TODO: Show all possible ORM filters for this field # This code has been mostly sucked from the tastypie lib if getattr(self.resource._meta, "queryset", None) is not None: # Get the possible query terms from the current QuerySet. if hasattr(self.resource._meta.queryset.query.query_terms, "keys"): # Django 1.4 & below compatibility. field = self.resource._meta.queryset.query.query_terms.keys() else: # Django 1.5+. field = self.resource._meta.queryset.query.query_terms else: if hasattr(QUERY_TERMS, "keys"): # Django 1.4 & below compatibility. field = QUERY_TERMS.keys() else: # Django 1.5+. field = QUERY_TERMS elif field == ALL_WITH_RELATIONS: # Show all params from related model # Add a subset of filter only foreign-key compatible on the relation itself. # We assume foreign keys are only int based. field = [ "gt", "in", "gte", "lt", "lte", "exact", ] # TODO This could be extended by checking the actual type of the relational field, but afaik it's also an issue on tastypie. related_resource = self.resource.fields[name].get_related_resource(None) related_mapping = ResourceSwaggerMapping(related_resource) parameters.extend( related_mapping.build_parameters_from_filters(prefix="%s%s__" % (prefix, name)) ) if isinstance(field, list): # Skip if this is an incorrect filter if name not in self.schema["fields"]: continue schema_field = self.schema["fields"][name] for query in field: if query == "exact": description = force_unicode(schema_field["help_text"]) dataType = schema_field["type"] # Use a better description for related models with exact filter if dataType == "related": # Assume that related pk is an integer # TODO if youre not using integer ID for pk then we need to look this up somehow dataType = "integer" description = "ID of related resource" parameters.append( self.build_parameter( paramType="query", name="%s%s" % (prefix, name), dataType=dataType, required=False, description=description, ) ) else: parameters.append( self.build_parameter( paramType="query", name="%s%s__%s" % (prefix, name, query), dataType=schema_field["type"], required=False, description=force_unicode(schema_field["help_text"]), ) ) return parameters
from django.utils.translation import ugettext_lazy as _ from django_filters.fields import NumericRangeField, DateRangeField, TimeRangeField, LookupTypeField __all__ = [ 'Filter', 'CharFilter', 'BooleanFilter', 'ChoiceFilter', 'MultipleChoiceFilter', 'DateFilter', 'DateTimeFilter', 'TimeFilter', 'ModelChoiceFilter', 'ModelMultipleChoiceFilter', 'NumberFilter', 'RangeFilter', 'DateRangeFilter', 'AllValuesFilter', 'OpenRangeNumericFilter', 'OpenRangeDateFilter', 'OpenRangeTimeFilter' ] LOOKUP_TYPES = None try: LOOKUP_TYPES = sorted(QUERY_TERMS.keys()) # Django 1.4 except AttributeError: LOOKUP_TYPES = sorted(list(QUERY_TERMS)) # Django >= 1.5 class Filter(object): creation_counter = 0 field_class = forms.Field def __init__(self, name=None, label=None, widget=None, action=None, lookup_type='exact', required=False, **kwargs): self.name = name self.label = label if action: self.filter = action self.lookup_type = lookup_type self.widget = widget
def build_parameters_from_filters(self, prefix="", method='GET'): parameters = [] # Deal with the navigational filters. # Always add the limits & offset params on the root ( aka not prefixed ) object. if not prefix and method.upper() == 'GET': navigation_filters = [ ('limit', 'int', 'Specify the number of element to display per page.'), ('offset', 'int', 'Specify the offset to start displaying element on a page.'), ] for name, type, desc in navigation_filters: parameters.append( self.build_parameter( paramType="query", name=name, dataType=type, required=False, description=force_unicode(desc), )) if 'filtering' in self.schema and method.upper() == 'GET': for name, field in self.schema['filtering'].items(): # Integer value means this points to a related model if field in [ALL, ALL_WITH_RELATIONS]: if field == ALL: #TODO: Show all possible ORM filters for this field #This code has been mostly sucked from the tastypie lib if getattr(self.resource._meta, 'queryset', None) is not None: # Get the possible query terms from the current QuerySet. if hasattr( self.resource._meta.queryset.query. query_terms, 'keys'): # Django 1.4 & below compatibility. field = self.resource._meta.queryset.query.query_terms.keys( ) else: # Django 1.5+. field = self.resource._meta.queryset.query.query_terms else: if hasattr(QUERY_TERMS, 'keys'): # Django 1.4 & below compatibility. field = QUERY_TERMS.keys() else: # Django 1.5+. field = QUERY_TERMS elif field == ALL_WITH_RELATIONS: # Show all params from related model # Add a subset of filter only foreign-key compatible on the relation itself. # We assume foreign keys are only int based. field = [ 'gt', 'in', 'gte', 'lt', 'lte', 'exact' ] # TODO This could be extended by checking the actual type of the relational field, but afaik it's also an issue on tastypie. related_resource = self.resource.fields[ name].get_related_resource(None) related_mapping = ResourceSwaggerMapping( related_resource) parameters.extend( related_mapping.build_parameters_from_filters( prefix="%s%s__" % (prefix, name))) elif is_sequence(field): # Skip if this is an incorrect filter if name not in self.schema['fields']: continue schema_field = self.schema['fields'][name] for query in field: if query == 'exact': description = force_unicode( schema_field['help_text']) dataType = schema_field['type'] # Use a better description for related models with exact filter if dataType == 'related': # Assume that related pk is an integer # TODO if youre not using integer ID for pk then we need to look this up somehow dataType = 'integer' description = 'ID of related resource' parameters.append( self.build_parameter( paramType="query", name="%s%s" % (prefix, name), dataType=dataType, required=False, description=description, )) else: parameters.append( self.build_parameter( paramType="query", name="%s%s__%s" % (prefix, name, query), dataType=schema_field['type'], required=False, description=force_unicode( schema_field['help_text']), )) return parameters
def build_parameters_from_filters(self, prefix="", method='GET'): parameters = [] # Deal with the navigational filters. # Always add the limits & offset params on the root ( aka not prefixed ) object. if not prefix and method.upper() == 'GET': navigation_filters = [ ('limit','int','Specify the number of element to display per page.'), ('offset','int','Specify the offset to start displaying element on a page.'), ] for name, type, desc in navigation_filters: parameters.append(self.build_parameter( paramType="query", name=name, dataType=type, required=False, description=force_unicode(desc), )) if 'filtering' in self.schema and method.upper() == 'GET': for name, field in self.schema['filtering'].items(): # Integer value means this points to a related model if field in [ALL, ALL_WITH_RELATIONS]: if field == ALL: #TODO: Show all possible ORM filters for this field #This code has been mostly sucked from the tastypie lib if getattr(self.resource._meta, 'queryset', None) is not None: # Get the possible query terms from the current QuerySet. if hasattr(self.resource._meta.queryset.query.query_terms, 'keys'): # Django 1.4 & below compatibility. field = self.resource._meta.queryset.query.query_terms.keys() else: # Django 1.5+. field = self.resource._meta.queryset.query.query_terms else: if hasattr(QUERY_TERMS, 'keys'): # Django 1.4 & below compatibility. field = QUERY_TERMS.keys() else: # Django 1.5+. field = QUERY_TERMS elif field == ALL_WITH_RELATIONS: # Show all params from related model try: related_resource = self.resource.fields[name].get_related_resource(None) related_mapping = ResourceSwaggerMapping(related_resource) if related_mapping.resource_name != self.resource_name: parameters.extend(related_mapping.build_parameters_from_filters(prefix="%s%s__" % (prefix, related_mapping.resource_name))) except (AttributeError, KeyError), e: pass elif isinstance( field, list ): # Skip if this is an incorrect filter if name not in self.schema['fields']: continue schema_field = self.schema['fields'][name] for query in field: if query == 'exact': description = force_unicode(schema_field['help_text']) dataType = schema_field['type'] # Use a better description for related models with exact filter if dataType == 'related': # Assume that related pk is an integer # TODO if youre not using integer ID for pk then we need to look this up somehow dataType = 'integer' description = 'ID of related resource' parameters.append(self.build_parameter( paramType="query", name="%s%s" % (prefix, name), dataType=dataType, required= False, description=description, )) else: parameters.append(self.build_parameter( paramType="query", name="%s%s__%s" % (prefix, name, query), dataType=schema_field['type'], required= False, description=force_unicode(schema_field['help_text']), ))