def test_query_aliases(self): qp = QueryParser(load_django_funcs=False) r = qp.parse_query("SomeModel") self.assertDictEqual(r.aliases, {}) r = qp.parse_query("SomeModel alias enfants = .children") self.assertDictEqual(r.aliases, {'enfants': 'children'})
def test_query_contains_cs_filter(self): qp = QueryParser(load_django_funcs=False) r = qp.parse_query("SomeModel .text_field contains 'oo'") self.assertEqual(r.queryset.count(), 1) r = qp.parse_query("SomeModel .text_field does not contain 'oo'") self.assertEqual(r.queryset.count(), 2)
def test_query_model_alias(self): qp = QueryParser(load_django_funcs=False) r = qp.parse_query("SomeModel") self.assertEqual(r.model_name, 'somemodel') r = qp.parse_query("SomeModel as meh") self.assertEqual(r.model_name, 'meh')
def test_query_and_filter(self): qp = QueryParser(load_django_funcs=False) r = qp.parse_query("SomeModel .int_field = 42 .text_field = 'foo'") self.assertEqual(r.queryset.count(), 1) r = qp.parse_query("SomeModel .int_field = 42 .text_field = 'not foo'") self.assertEqual(r.queryset.count(), 0)
def execute(query): result = QueryParser().parse_query(query) qs = result.queryset if len(qs) <= 0: raise ParseError(_("The query must be non empty.")) user_qs = qs User = get_user_model() if user_qs.model != User and 'user' in result.aliases: user_field = result.aliases['user'] try: if get_field_rec(qs.model, user_field) != User: raise ParseError( _("%(label)s.%(field)s is not %(model)s") % { 'label': qs_label, 'field': user_field, 'model': user_label, }) except FieldDoesNotExist: raise ParseError( _("%(label)s has no field `%(field)s`" % { 'label': qs_label, 'field': user_field })) user_pks = set(qs.values_list(user_field, flat=True)) user_qs = User._default_manager.filter(pk__in=user_pks) if 'email' not in result.aliases and hasattr(user_qs[0], 'email'): result.aliases['email'] = 'email' if 'email' not in result.aliases and not hasattr(user_qs[0], 'email'): raise ParseError( _("The query must have an email field or declare an `email` alias." )) return result, user_qs
def test_query_matches(self): qp = QueryParser(load_django_funcs=False) r = qp.parse_query("SomeModel .text_field matches '[oO]{2}'") self.assertEqual(r.queryset.count(), 2) r = qp.parse_query("SomeModel .text_field does not match '[oO]{2}'") self.assertEqual(r.queryset.count(), 1) self.assertEqual(r.queryset.first().int_field, 3)
def test_query_ends_with(self): qp = QueryParser(load_django_funcs=False) r = qp.parse_query("SomeModel .text_field end with 'oo'") self.assertEqual(r.queryset.count(), 2) r = qp.parse_query("SomeModel .text_field does not end with 'oo'") self.assertEqual(r.queryset.count(), 1) self.assertEqual(r.queryset.first().int_field, 3)
def test_query_starts_with(self): qp = QueryParser(load_django_funcs=False) r = qp.parse_query("SomeModel .text_field starts with 'bar'") self.assertEqual(r.queryset.count(), 1) self.assertEqual(r.queryset.first().text_field, "BAROO") r = qp.parse_query("SomeModel .text_field does not start with 'bar'") self.assertEqual(r.queryset.count(), 2)
def test_query_bool_field_predicate(self): qp = QueryParser(load_django_funcs=False) r = qp.parse_query("SomeModel .bool_field = true") self.assertEqual(r.queryset.count(), 1) self.assertEqual(r.queryset.first().bool_field, True) r = qp.parse_query("SomeModel .bool_field = False") self.assertEqual(r.queryset.count(), 1) self.assertEqual(r.queryset.first().bool_field, False)
def test_query_func_substr(self): qp = QueryParser() r = qp.parse_query("SomeModel substr(.text_field, 1, 2) = 'fo'") self.assertEqual(r.queryset.count(), 1) self.assertEqual(r.queryset.first().text_field, 'foo') r = qp.parse_query("SomeModel length(.text_field) = .int_field") self.assertEqual(r.queryset.count(), 1) self.assertEqual(r.queryset.first().text_field, 'coq')
def test_query_negation(self): qp = QueryParser(load_django_funcs=False) r = qp.parse_query("SomeModel not .int_field = 42") self.assertEqual(r.queryset.count(), 2) r = qp.parse_query("SomeModel not not .int_field = 42") self.assertEqual(r.queryset.count(), 1) r = qp.parse_query("SomeModel not not not .int_field = 42") self.assertEqual(r.queryset.count(), 2)
def test_query_func_count(self): qp = QueryParser() r = qp.parse_query("SomeModel count(.children) = 0") self.assertEqual(r.queryset.count(), 2) r = qp.parse_query("SomeModel count(.children) = 1") self.assertEqual(r.queryset.count(), 0) r = qp.parse_query("SomeModel count(.children) = 2") self.assertEqual(r.queryset.count(), 1) self.assertEqual(r.queryset.first().text_field, 'foo')
def test_query_no_such_enum_member(self): from tests.models import SomeEnum qp = QueryParser(load_django_funcs=False) # Register the enum qp.available_enums['MyApp.SomeEnum'] = SomeEnum with self.assertRaisesRegex( ParseError, r"SomeEnum.+no member.+garbage" ): qp.parse_query("SomeModel .text_field = MyApp.SomeEnum.garbage")
def test_query_enum(self): from tests.models import SomeEnum qp = QueryParser(load_django_funcs=False) # Register the enum qp.available_enums['MyApp.SomeEnum'] = SomeEnum r = qp.parse_query("SomeModel .int_field = MyApp.SomeEnum.foo") self.assertEqual(r.queryset.count(), 1) self.assertEqual(r.queryset.first().int_field, SomeEnum.foo.value) r = qp.parse_query("SomeModel .text_field = MyApp.SomeEnum.bar") self.assertEqual(r.queryset.count(), 1) self.assertEqual(r.queryset.first().text_field, SomeEnum.bar.value)
def execute(query): User = get_user_model() user_label = User._meta.label result = QueryParser().parse_query(query) qs = result.queryset qs_label = qs.model._meta.label user_qs = qs # if queryset is not a User queryset, require a "user" alias if user_qs.model != User: if 'user' not in result.aliases: raise ParseError( _("The root model is not %(model)s. You must provide a `user` alias." ) % {'model': user_label}) user_field = result.aliases['user'] try: if get_field_rec(qs.model, user_field) != User: raise ParseError( _("%(label)s.%(field)s is not %(model)s") % { 'label': qs_label, 'field': user_field, 'model': user_label, }) except FieldDoesNotExist: raise ParseError( _("%(label)s has no field `%(field)s`" % { 'label': qs_label, 'field': user_field })) user_pks = set(qs.values_list(user_field, flat=True)) user_qs = User._default_manager.filter(pk__in=user_pks) return result, user_qs
def available_enums(self): return sorted( ( {'name': name, 'members': [m.name for m in enum]} for name, enum in QueryParser().available_enums.items() ), key=lambda e: e['name'].lower(), )
def available_funcs(self): return [ { 'name': name, 'doc': inspect.getdoc(func), 'signature': inspect.signature(func), } for name, func in QueryParser().available_funcs.items() ]
def test_query_between_filter(self): qp = QueryParser() r = qp.parse_query("SomeModel .int_field between 42 and 50") self.assertEqual(r.queryset.count(), 1) self.assertEqual(r.queryset.first().int_field, 42) r = qp.parse_query("SomeModel .int_field between 0 and 41") self.assertEqual(r.queryset.count(), 1) r = qp.parse_query("SomeModel count(.children) between -1 and 1") self.assertEqual(r.queryset.count(), 2) r = qp.parse_query("SomeModel .int_field not between 42 and 1337") self.assertEqual(r.queryset.count(), 1) self.assertEqual(r.queryset.first().int_field, 3) # with self.assertRaises(TypeError, "'between' does not support"): qp.parse_query("SomeModel .int_field between true and false")
def test_query_comparators(self): qp = QueryParser(load_django_funcs=False) r = qp.parse_query("SomeModel .int_field > 42") self.assertEqual(r.queryset.count(), 1) r = qp.parse_query("SomeModel .int_field >= 42") self.assertEqual(r.queryset.count(), 2) r = qp.parse_query("SomeModel .int_field < 1337") self.assertEqual(r.queryset.count(), 2) self.assertEqual(r.queryset.first().text_field, "foo") r = qp.parse_query("SomeModel .int_field <= 1337") self.assertEqual(r.queryset.count(), 3) r = qp.parse_query("SomeModel .int_field != 3") self.assertEqual(r.queryset.count(), 2)
def test_query_arithmetic(self): qp = QueryParser() r = qp.parse_query("SomeModel .int_field = 0b101010") self.assertEqual(r.queryset.first().int_field, 42) r = qp.parse_query("SomeModel .int_field = 0x2a") self.assertEqual(r.queryset.first().int_field, 42) r = qp.parse_query("SomeModel .int_field = 0o52") self.assertEqual(r.queryset.first().int_field, 42) r = qp.parse_query("SomeModel .int_field = -(-4.2e1)") self.assertEqual(r.queryset.first().int_field, 42) r = qp.parse_query("SomeModel .int_field = (20 * 2**2 / 2) + 4 - 2") self.assertEqual(r.queryset.first().int_field, 42) r = qp.parse_query( "SomeModel .other_text = concat('qux', .text_field)" ) self.assertEqual(r.queryset.first().other_text, "quxcoq")
def test_query_null_empty(self): qp = QueryParser(load_django_funcs=False) r = qp.parse_query("SomeModel .bool_field is null") self.assertEqual(r.queryset.count(), 1) self.assertEqual(r.queryset.first().text_field, "coq") r = qp.parse_query("SomeModel .bool_field is not null") self.assertEqual(r.queryset.count(), 2) r = qp.parse_query("SomeModel .other_text is empty") self.assertEqual(r.queryset.count(), 1) r = qp.parse_query("SomeModel .text_field is not empty") self.assertEqual(r.queryset.count(), 3)
def test_query_no_such_func(self): qp = QueryParser(load_django_funcs=False) with self.assertRaisesRegex(ParseError, r'Unknown.+garbage'): qp.parse_query("SomeModel garbage(.text_field) = 1")
def test_query_int_field_predicate(self): qp = QueryParser(load_django_funcs=False) r = qp.parse_query("SomeModel .int_field = 42") self.assertEqual(r.queryset.count(), 1) self.assertEqual(r.queryset.first().int_field, 42)
def test_query_no_such_model(self): qp = QueryParser(load_django_funcs=False) with self.assertRaisesRegex(ParseError, 'Unknown.+model.+Garbage'): qp.parse_query("Garbage")
def test_query_comment(self): qp = QueryParser(load_django_funcs=False) r = qp.parse_query("# a comment\nSomeModel") self.assertEqual(r.queryset.count(), 3)
def test_django_models_loaded(self): from tests.models import SomeModel qp = QueryParser(load_django_funcs=False) self.assertIn('SomeModel', qp.available_models) self.assertIs(qp.available_models['SomeModel'], SomeModel)
def test_query_field_as_value(self): qp = QueryParser(load_django_funcs=False) r = qp.parse_query("SomeModel .text_field = .other_text") self.assertEqual(r.queryset.count(), 1) self.assertEqual(r.queryset.first().text_field, "foo")
def test_django_func_registry(self): qp = QueryParser(load_django_models=False) self.assertIn('count', qp.available_funcs) self.assertIn('avg', qp.available_funcs) self.assertIn('min', qp.available_funcs) self.assertIn('max', qp.available_funcs)
def test_query_no_such_enum(self): qp = QueryParser(load_django_funcs=False) with self.assertRaisesRegex(ParseError, r"Unknown.+Do\.Not"): qp.parse_query("SomeModel .text_field = Do.Not.Exists")
def test_django_manual_registry(self): qp = QueryParser(load_django_funcs=False, load_django_models=False) self.assertNotIn('SomeModel', qp.available_models)