diff --git a/binder/models.py b/binder/models.py index 53a95639..4e2c9966 100644 --- a/binder/models.py +++ b/binder/models.py @@ -239,11 +239,14 @@ class IntegerFieldFilter(FieldFilter): models.ManyToManyField, models.ManyToManyRel, ] - allowed_qualifiers = [None, 'in', 'gt', 'gte', 'lt', 'lte', 'range', 'isnull'] + allowed_qualifiers = [None, 'in', 'gt', 'gte', 'lt', 'lte', 'range', 'isnull', 'icontains'] def clean_value(self, qualifier, v): try: - return int(v) + if qualifier == 'icontains': + return str(v) + else: + return int(v) except ValueError: raise ValidationError('Invalid value {{{}}} for {}.'.format(v, self.field_description())) diff --git a/docker-compose.yml b/docker-compose.yml index b0aeb0c7..d489fdf6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,11 @@ services: db: - image: postgres:11.5 + restart: always + image: postgres:12 + environment: + - POSTGRES_HOST_AUTH_METHOD=trust binder: + restart: always build: . command: tail -f /dev/null volumes: diff --git a/tests/filters/test_integer_icontains.py b/tests/filters/test_integer_icontains.py new file mode 100644 index 00000000..2761c50d --- /dev/null +++ b/tests/filters/test_integer_icontains.py @@ -0,0 +1,63 @@ +import unittest, os + +from django.test import TestCase, Client +from django.contrib.auth.models import User + +from binder.json import jsonloads + +from ..testapp.models import Zoo, ZooEmployee + + +@unittest.skipIf( + os.environ.get('BINDER_TEST_MYSQL', '0') != '0', + "Only available with PostgreSQL" +) +class IntegerIcontainsTest(TestCase): + + def setUp(self): + super().setUp() + u = User(username='testuser', is_active=True, is_superuser=True) + u.set_password('test') + u.save() + + self.client = Client() + r = self.client.login(username='testuser', password='test') + self.assertTrue(r) + + zoo = Zoo(name='Yes') + zoo.save() + + ZooEmployee(name='Small Number Fan', favorite_number='3', zoo=zoo).save() + ZooEmployee(name='Big Number Enjoyer', favorite_number='100023', zoo=zoo).save() + ZooEmployee(name='Bob', favorite_number='101', zoo=zoo).save() + + def test_filter_partial_match(self): + response = self.client.get('/zoo_employee/', data={'.favorite_number:icontains': '3'}) + + self.assertEqual(response.status_code, 200) + + result = jsonloads(response.content) + self.assertEqual(2, len(result['data'])) + self.assertEqual('Small Number Fan', result['data'][0]['name']) + self.assertEqual('Big Number Enjoyer', result['data'][1]['name']) + + response = self.client.get('/zoo_employee/', data={'.favorite_number:icontains': '100'}) + + result = jsonloads(response.content) + self.assertEqual(1, len(result['data'])) + self.assertEqual('Big Number Enjoyer', result['data'][0]['name']) + + def test_filter_exact_match(self): + response = self.client.get('/zoo_employee/', data={'.favorite_number': '3'}) + + self.assertEqual(response.status_code, 200) + + result = jsonloads(response.content) + self.assertEqual(1, len(result['data'])) + self.assertEqual('Small Number Fan', result['data'][0]['name']) + + response = self.client.get('/zoo_employee/', data={'.favorite_number': '101'}) + + result = jsonloads(response.content) + self.assertEqual(1, len(result['data'])) + self.assertEqual('Bob', result['data'][0]['name']) diff --git a/tests/testapp/models/zoo_employee.py b/tests/testapp/models/zoo_employee.py index 12ec8abc..9dd6939b 100644 --- a/tests/testapp/models/zoo_employee.py +++ b/tests/testapp/models/zoo_employee.py @@ -7,6 +7,7 @@ class ZooEmployee(LoadedValuesMixin, BinderModel): name = models.TextField(max_length=64) zoo = models.ForeignKey('Zoo', on_delete=models.CASCADE, related_name='zoo_employees') hourly_wage = models.DecimalField(max_digits=4, decimal_places=2, null=True, blank=True) + favorite_number = models.IntegerField(null=True, blank=True) deleted = models.BooleanField(default=False) # Softdelete def __str__(self):