diff --git a/ckanext/datastore/db.py b/ckanext/datastore/db.py index 53775000752..ce9707d6625 100644 --- a/ckanext/datastore/db.py +++ b/ckanext/datastore/db.py @@ -17,6 +17,7 @@ import psycopg2.extras import ckan.lib.cli as cli import ckan.plugins.toolkit as toolkit +import ckanext.datastore.interfaces as interfaces log = logging.getLogger(__name__) @@ -757,6 +758,12 @@ def _where(field_ids, data_dict): where_clauses = [] values = [] + for plugin in p.PluginImplementations(interfaces.IDataStore): + filters, clauses = plugin.where(filters) + for clause in clauses: + where_clauses.append(clause[0]) + values += clause[1:] + for field, value in filters.iteritems(): if field not in field_ids: raise ValidationError({ diff --git a/ckanext/datastore/interfaces.py b/ckanext/datastore/interfaces.py new file mode 100644 index 00000000000..fec9c008079 --- /dev/null +++ b/ckanext/datastore/interfaces.py @@ -0,0 +1,17 @@ +import ckan.plugins.interfaces as interfaces + + +class IDataStore(interfaces.Interface): + '''Allow changing DataStore queries''' + + def where(self, filters): + ''' + :param filters: dictionary with non-processed filters + :type filters: dictionary + + :returns: the filters dictionary with the elements that were processed + by this method removed, and the relative clauses list created + from them. + ''' + clauses = [] + return filters, clauses diff --git a/ckanext/datastore/tests/test_interface.py b/ckanext/datastore/tests/test_interface.py new file mode 100644 index 00000000000..e16a46ec5e6 --- /dev/null +++ b/ckanext/datastore/tests/test_interface.py @@ -0,0 +1,71 @@ +import nose + +import ckan.plugins as p +import ckan.new_tests.helpers as helpers +import ckan.new_tests.factories as factories + +import ckanext.datastore.interfaces as interfaces + +assert_equals = nose.tools.assert_equals + + +class SampleDataStorePlugin(p.SingletonPlugin): + p.implements(interfaces.IDataStore) + + def where(self, filters): + clauses = [] + if 'age_between' in filters: + age_between = filters['age_between'] + del filters['age_between'] + + clause = ('"age" >= %s AND "age" <= %s', + age_between[0], age_between[1]) + clauses.append(clause) + return filters, clauses + + +class TestInterfaces(object): + @classmethod + def setup_class(cls): + cls.plugin = SampleDataStorePlugin() + p.load('datastore') + + @classmethod + def teardown_class(cls): + p.unload('datastore') + + def setup(self): + self.plugin.activate() + helpers.reset_db() + + def teardown(self): + self.plugin.deactivate() + + def test_can_create_custom_filters(self): + records = [ + {'age': 20}, {'age': 27}, {'age': 33} + ] + resource = self._create_datastore_resource(records) + filters = {'age_between': [25, 30]} + + result = helpers.call_action('datastore_search', + resource_id=resource['id'], + filters=filters) + + assert result['total'] == 1, result + assert result['records'][0]['age'] == 27, result + assert_equals(result['filters'], filters) + + def _create_datastore_resource(self, records): + dataset = factories.Dataset() + resource = factories.Resource(package=dataset) + + data = { + 'resource_id': resource['id'], + 'force': True, + 'records': records + } + + helpers.call_action('datastore_create', **data) + + return resource