def monkey_patch_where_node(): global monkey_patched if monkey_patched is True: return monkey_patched = True if django.VERSION >= (1,5): from django.db.models.sql.constants import QUERY_TERMS QUERY_TERMS.update(UnaccentOperation.operators.keys() + UnaccentOperation.smart_operators) # Update the operators accepted by a query when adding filters by adding those of unaccent # This way, it passes the test at db/models/sql/query.py:1021 which otherwise will override our custom lookup_types Query.query_terms.update(izip(UnaccentOperation.operators.keys() + UnaccentOperation.smart_operators, repeat(None))) WhereNode.add = patched_wherenode_add
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 _expand_money_params(kwargs): from moneyed import Money from django.db.models.sql.constants import LOOKUP_SEP, QUERY_TERMS to_append = {} for name, value in kwargs.items(): if isinstance(value, Money): # Get rid of __lt, __gt etc for the currency lookup path = name.split(LOOKUP_SEP) if QUERY_TERMS.has_key(path[-1]): clean_name = LOOKUP_SEP.join(path[:-1]) else: clean_name = name to_append[name] = value.amount to_append[currency_field_name(clean_name)] = smart_unicode(value.currency) kwargs.update(to_append) return kwargs
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 _expand_money_params(kwargs): from moneyed import Money from django.db.models.sql.constants import LOOKUP_SEP, QUERY_TERMS to_append = {} for name, value in kwargs.items(): if isinstance(value, Money): # Get rid of __lt, __gt etc for the currency lookup path = name.split(LOOKUP_SEP) if QUERY_TERMS.has_key(path[-1]): clean_name = LOOKUP_SEP.join(path[:-1]) else: clean_name = name to_append[name] = value.amount to_append[currency_field_name(clean_name)] = smart_unicode( value.currency) kwargs.update(to_append) return kwargs
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_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)
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(): # 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']), ))
'center', # center(object) 'area', # area(object) 'area_gt', 'area_lt', 'area_gte', 'area_lte', 'overlap', 'strictly_left_of', 'strictly_right_of', 'notextendto_right_of', 'notextendto_left_of', 'strictly_below', 'strictly_above', 'notextend_above', 'notextend_below', 'is_below', 'is_above', 'intersects', 'is_horizontal', 'is_perpendicular', 'is_parallel', 'contained_in_or_on', 'contains', 'same_as', ) QUERY_TERMS.update(dict([(x, None) for x in \ ('indexexact', 'distinct', 'slice', 'containedby', 'unaccent', 'iunaccent', 'query')])) QUERY_TERMS.update(dict([(x, None) for x in geometric_lookups]))
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)) ) 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
return value_returned def monkey_get_db_prep_lookup(cls): cls.get_db_prep_lookup_origin = cls.get_db_prep_lookup cls.get_db_prep_lookup = get_db_prep_lookup if hasattr(subclassing, "call_with_connection_and_prepared"): # Dj > 1.1 setattr(cls, "get_db_prep_lookup", subclassing.call_with_connection_and_prepared(cls.get_db_prep_lookup)) for new_cls in cls.__subclasses__(): monkey_get_db_prep_lookup(new_cls) if backend_allowed: if isinstance(QUERY_TERMS, set): QUERY_TERMS.add("similar") else: QUERY_TERMS["similar"] = None if backend_allowed == "postgis": if isinstance(ALL_TERMS, set): ALL_TERMS.add("similar") else: ALL_TERMS["similar"] = None connection.operators["similar"] = "%%%% %s" NEW_LOOKUP_TYPE = ("similar",) monkey_get_db_prep_lookup(Field) if hasattr(Field, "get_prep_lookup"):
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)
# You should have received a copy of the GNU Lesser General Public License # along with this software. If not, see <http://www.gnu.org/licenses/>. import django import datetime from django.conf import settings from django.contrib.auth.models import User from django.core.exceptions import FieldError from django.core.management import call_command from django.core.urlresolvers import reverse from django.db.models.sql.constants import QUERY_TERMS from django.test import TestCase if isinstance(QUERY_TERMS, set): QUERY_TERMS.add('error') else: QUERY_TERMS['error'] = None class DjangoLikeTestCase(TestCase): fixtures = ['app_data.json'] def test_like(self): users_like = User.objects.filter(username__like="u%r%") users_regex = User.objects.filter(username__regex="^u.*r.$") self.assertEqual(list(users_like), list(users_regex)) self.assertEqual(users_like.count(), 4) def test_ilike(self):
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
if backend_allowed and 'Lookup' in locals(): # Use Django 1.7 API for registering a new lookup class Similar(Lookup): lookup_name = 'similar' def as_sql(self, qn, connection): lhs, lhs_params = self.process_lhs(qn, connection) rhs, rhs_params = self.process_rhs(qn, connection) params = lhs_params + rhs_params return '%s %%%% %s' % (lhs, rhs), params Field.register_lookup(Similar) elif backend_allowed: # Old pre-Django 1.7 manual injection of lookup if isinstance(QUERY_TERMS, set): QUERY_TERMS.add('similar') else: QUERY_TERMS['similar'] = None if backend_allowed == 'postgis': if isinstance(ALL_TERMS, set): ALL_TERMS.add('similar') else: ALL_TERMS['similar'] = None connection.operators['similar'] = "%%%% %s" NEW_LOOKUP_TYPE = ('similar', ) monkey_get_db_prep_lookup(Field) if hasattr(Field, 'get_prep_lookup'):
lhs_sql = 'UPPER(%s)' % lhs_sql return (lhs_sql, params) def get_rhs_op(self, connection, rhs): return connection.operators['icontains'] % rhs Field.register_lookup(Like) Field.register_lookup(ILike) else: from django.db import connection from django.db.models.fields import Field, subclassing from django.db.models.sql.constants import QUERY_TERMS if isinstance(QUERY_TERMS, set): QUERY_TERMS.add('like') QUERY_TERMS.add('ilike') else: QUERY_TERMS['like'] = None QUERY_TERMS['ilike'] = None connection.operators['like'] = connection.operators['contains'] connection.operators['ilike'] = connection.operators['icontains'] NEW_LOOKUP_TYPE = ('like', 'ilike') def get_prep_lookup(self, lookup_type, value): try: return self.get_prep_lookup_origin(lookup_type, value) except TypeError as e: if lookup_type in NEW_LOOKUP_TYPE: return value
# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. from django.contrib.gis.db import models from django.db.models.sql.constants import QUERY_TERMS from django.db.backends.postgresql_psycopg2.base import DatabaseWrapper from django.db.backends.postgresql_psycopg2.operations import ( DatabaseOperations) QUERY_TERMS.add('unaccent') DatabaseWrapper.operators['unaccent'] = '=~@ %s' def my_lookup_cast(self, lookup_type): """ Adding 'unaccent' to the standard lookup_cast """ lookup = '%s' # Cast text lookups to text to allow things like filter(x__contains=4) if lookup_type in ('iexact', 'contains', 'icontains', 'unaccent', 'startswith', 'istartswith', 'endswith', 'iendswith'): lookup = "%s::text" # Use UPPER(x) for case-insensitive lookups; it's faster.
'strictly_above', 'notextend_above', 'notextend_below', 'is_below', 'is_above', 'intersects', 'is_horizontal', 'is_perpendicular', 'is_parallel', 'contained_in_or_on', 'same_as', 'contains', 'center', ) GEOMETRIC_LOOKUPS = dict((x, None) for x in geometric_lookups) FTS_LOCKUPS = {'query': None, 'query_raw':None} VARCHAR_LOOKUPS = dict((x, None) for x in ('unaccent', 'iunaccent')) INTERVAL_LOOKUPS = dict((x, None) for x in ('distinct', 'exact', 'gt','lt','gte', 'lte')) ARRAY_LOOKUPS = dict((x, None) for x in ('indexexact', 'distinct', 'slice', 'contains', 'containedby', 'overlap', 'exact', 'gt','lt','gte', 'lte')) QUERY_TERMS.update(GEOMETRIC_LOOKUPS) QUERY_TERMS.update(FTS_LOCKUPS) QUERY_TERMS.update(VARCHAR_LOOKUPS) QUERY_TERMS.update(INTERVAL_LOOKUPS) QUERY_TERMS.update(ARRAY_LOOKUPS) GEOMETRIC_TYPES = dict((x, None) for x in \ ('box', 'point', 'line', 'lseg', 'path', 'polygon', 'circle'))
# along with this software. If not, see <http://www.gnu.org/licenses/>. import django import datetime from django.conf import settings from django.contrib.auth.models import User from django.core.exceptions import FieldError from django.core.management import call_command from django.core.urlresolvers import reverse from django.db.models.sql.constants import QUERY_TERMS from django.test import TestCase if isinstance(QUERY_TERMS, set): QUERY_TERMS.add('error') else: QUERY_TERMS['error'] = None class DjangoLikeTestCase(TestCase): fixtures = ['app_data.json'] def test_like(self): users_like = User.objects.filter(username__like="u%r%") users_regex = User.objects.filter(username__regex="^u.*r.$") self.assertEqual(list(users_like), list(users_regex)) self.assertEqual(users_like.count(), 4) def test_ilike(self):
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(): # 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 get_rhs_op(self, connection, rhs): return connection.operators['icontains'] % rhs Field.register_lookup(Like) Field.register_lookup(ILike) else: from django.db import backend from django.db import connection from django.db.models.fields import Field, subclassing from django.db.models.sql.constants import QUERY_TERMS if isinstance(QUERY_TERMS, set): QUERY_TERMS.add('like') QUERY_TERMS.add('ilike') else: QUERY_TERMS['like'] = None QUERY_TERMS['ilike'] = None connection.operators['like'] = connection.operators['contains'] connection.operators['ilike'] = connection.operators['icontains'] NEW_LOOKUP_TYPE = ('like', 'ilike') def get_prep_lookup(self, lookup_type, value): try: return self.get_prep_lookup_origin(lookup_type, value) except TypeError as e: if lookup_type in NEW_LOOKUP_TYPE: return value
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
# -*- coding: utf-8 -*- from django.db.models.sql.constants import QUERY_TERMS QUERY_TERMS.update(dict([(x, None) for x in ['unaccent', 'iunaccent']]))