diff --git a/README.rst b/README.rst index 7dba0f9..4898e5c 100644 --- a/README.rst +++ b/README.rst @@ -182,6 +182,29 @@ You can optionally pass conditions to the query:: Note that ``order_by`` must be chosen so that the ordering is unique, otherwise there might be inconsistencies in the pagination (such as an instance that appears on two different pages). +System models +------------- +`Clickhouse docs ` +System models are read only models for implementing part of the system's functionality, +and for providing access to information about how the system is working. + +Usage example: + + >>>> from infi.clickhouse_orm import system_models + >>>> print(system_models.SystemPart.all()) + +Currently the fllowing system models are supported: + +=================== ======== ================= =================================================== +Class DB Table Pythonic Type Comments +=================== ======== ================= =================================================== +SystemPart + +Partitions and parts +-------------------- +`ClickHouse docs ` + + Schema Migrations ----------------- diff --git a/src/infi/clickhouse_orm/fields.py b/src/infi/clickhouse_orm/fields.py index 46fb0c5..1b4ee7e 100644 --- a/src/infi/clickhouse_orm/fields.py +++ b/src/infi/clickhouse_orm/fields.py @@ -70,8 +70,9 @@ class Field(object): else: return self.db_type - def is_insertable(self): - return self.alias is None and self.materialized is None + @property + def readonly(self): + return self.alias is not None or self.materialized is not None class StringField(Field): diff --git a/src/infi/clickhouse_orm/models.py b/src/infi/clickhouse_orm/models.py index 70ccc34..9580079 100644 --- a/src/infi/clickhouse_orm/models.py +++ b/src/infi/clickhouse_orm/models.py @@ -159,9 +159,8 @@ class Model(with_metaclass(ModelBase)): :param bool insertable_only: If True, returns only fields, that can be inserted into database ''' data = self.__dict__ - fields = self._fields - if insertable_only: - fields = [f for f in fields if f[1].is_insertable()] + + fields = [f for f in self._fields if not f[1].readonly] if insertable_only else self._fields return '\t'.join(field.to_db_string(data[name], quote=False) for name, field in fields) def to_dict(self, insertable_only=False, field_names=None): @@ -170,7 +169,7 @@ class Model(with_metaclass(ModelBase)): :param bool insertable_only: If True, returns only fields, that can be inserted into database :param field_names: An iterable of field names to return ''' - fields = [f for f in self._fields if f[1].is_insertable()] if insertable_only else self._fields + fields = [f for f in self._fields if not f[1].readonly] if insertable_only else self._fields if field_names is not None: fields = [f for f in fields if f[0] in field_names] diff --git a/src/infi/clickhouse_orm/system_models.py b/src/infi/clickhouse_orm/system_models.py index 25ea0e0..ce94cb6 100644 --- a/src/infi/clickhouse_orm/system_models.py +++ b/src/infi/clickhouse_orm/system_models.py @@ -113,25 +113,30 @@ class SystemPart(Model): return self._partition_operation_sql(database, 'FETCH', settings=settings, from_part=zookeeper_path) @classmethod - def get_active(cls, database): + def get(cls, database, conditions=""): """ - Get all active parts + Get all data from system.parts table :param database: A database object to fetch data from. + :param conditions: WHERE clause conditions. Database condition is added automatically :return: A list of SystemPart objects """ assert isinstance(database, Database), "database must be database.Database class instance" + assert isinstance(conditions, str), "conditions must be a string" + if conditions: + conditions += " AND" field_names = ','.join([f[0] for f in cls._fields]) - return database.select("SELECT %s FROM %s WHERE active AND database='%s'" % - (field_names, cls.table_name(), database.db_name), model_class=cls) + return database.select("SELECT %s FROM %s WHERE %s database='%s'" % + (field_names, cls.table_name(), conditions, database.db_name), model_class=cls) @classmethod - def all(cls, database): + def get_active(cls, database, conditions=""): """ - Gets all data from system.parts database - :param database: - :return: + Gets active data from system.parts table + :param database: A database object to fetch data from. + :param conditions: WHERE clause conditions. Database and active conditions are added automatically + :return: A list of SystemPart objects """ - assert isinstance(database, Database), "database must be database.Database class instance" - field_names = ','.join([f[0] for f in cls._fields]) - return database.select("SELECT %s FROM %s WHERE database='%s'" % - (field_names, cls.table_name(), database.db_name), model_class=cls) + if conditions: + conditions += ' AND ' + conditions += 'active' + return SystemPart.get(database, conditions=conditions) diff --git a/tests/test_system_models.py b/tests/test_system_models.py index d8e075a..ac1616c 100644 --- a/tests/test_system_models.py +++ b/tests/test_system_models.py @@ -26,7 +26,7 @@ class SystemPartTest(unittest.TestCase): return dirnames def test_get_all(self): - parts = SystemPart.all(self.database) + parts = SystemPart.get(self.database) self.assertEqual(len(list(parts)), 1) def test_get_active(self): @@ -35,6 +35,12 @@ class SystemPartTest(unittest.TestCase): parts[0].detach(self.database) self.assertEqual(len(list(SystemPart.get_active(self.database))), 0) + def test_get_conditions(self): + parts = list(SystemPart.get(self.database, conditions="table='testtable'")) + self.assertEqual(len(parts), 1) + parts = list(SystemPart.get(self.database, conditions="table='othertable'")) + self.assertEqual(len(parts), 0) + def test_attach_detach(self): parts = list(SystemPart.get_active(self.database)) self.assertEqual(len(parts), 1) @@ -49,7 +55,7 @@ class SystemPartTest(unittest.TestCase): self.assertEqual(len(list(SystemPart.get_active(self.database))), 0) def test_freeze(self): - parts = list(SystemPart.all(self.database)) + parts = list(SystemPart.get(self.database)) # There can be other backups in the folder prev_backups = set(self._get_backups()) parts[0].freeze(self.database)