1) Replaced is_insertable() field mehtod with readonly property (unification with model and tables)

2) Method SystemPart.all() was replaced with get()
3) Added conditions parameter to SystemPart.get() and SystemPart.get_active() methods.
This commit is contained in:
M1ha 2017-02-08 10:31:19 +05:00 committed by Itai Shirav
parent 58b7a9aeac
commit 5f2195f87f
5 changed files with 54 additions and 20 deletions

View File

@ -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 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). inconsistencies in the pagination (such as an instance that appears on two different pages).
System models
-------------
`Clickhouse docs <https://clickhouse.yandex/reference_en.html#System tables>`
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 <https://clickhouse.yandex/reference_en.html#Manipulations with partitions and parts>`
Schema Migrations Schema Migrations
----------------- -----------------

View File

@ -70,8 +70,9 @@ class Field(object):
else: else:
return self.db_type return self.db_type
def is_insertable(self): @property
return self.alias is None and self.materialized is None def readonly(self):
return self.alias is not None or self.materialized is not None
class StringField(Field): class StringField(Field):

View File

@ -159,9 +159,8 @@ class Model(with_metaclass(ModelBase)):
:param bool insertable_only: If True, returns only fields, that can be inserted into database :param bool insertable_only: If True, returns only fields, that can be inserted into database
''' '''
data = self.__dict__ data = self.__dict__
fields = self._fields
if insertable_only: fields = [f for f in self._fields if not f[1].readonly] if insertable_only else self._fields
fields = [f for f in fields if f[1].is_insertable()]
return '\t'.join(field.to_db_string(data[name], quote=False) for name, field in 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): 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 bool insertable_only: If True, returns only fields, that can be inserted into database
:param field_names: An iterable of field names to return :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: if field_names is not None:
fields = [f for f in fields if f[0] in field_names] fields = [f for f in fields if f[0] in field_names]

View File

@ -113,25 +113,30 @@ class SystemPart(Model):
return self._partition_operation_sql(database, 'FETCH', settings=settings, from_part=zookeeper_path) return self._partition_operation_sql(database, 'FETCH', settings=settings, from_part=zookeeper_path)
@classmethod @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 database: A database object to fetch data from.
:param conditions: WHERE clause conditions. Database condition is added automatically
:return: A list of SystemPart objects :return: A list of SystemPart objects
""" """
assert isinstance(database, Database), "database must be database.Database class instance" 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]) field_names = ','.join([f[0] for f in cls._fields])
return database.select("SELECT %s FROM %s WHERE active AND database='%s'" % return database.select("SELECT %s FROM %s WHERE %s database='%s'" %
(field_names, cls.table_name(), database.db_name), model_class=cls) (field_names, cls.table_name(), conditions, database.db_name), model_class=cls)
@classmethod @classmethod
def all(cls, database): def get_active(cls, database, conditions=""):
""" """
Gets all data from system.parts database Gets active data from system.parts table
:param database: :param database: A database object to fetch data from.
:return: :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" if conditions:
field_names = ','.join([f[0] for f in cls._fields]) conditions += ' AND '
return database.select("SELECT %s FROM %s WHERE database='%s'" % conditions += 'active'
(field_names, cls.table_name(), database.db_name), model_class=cls) return SystemPart.get(database, conditions=conditions)

View File

@ -26,7 +26,7 @@ class SystemPartTest(unittest.TestCase):
return dirnames return dirnames
def test_get_all(self): def test_get_all(self):
parts = SystemPart.all(self.database) parts = SystemPart.get(self.database)
self.assertEqual(len(list(parts)), 1) self.assertEqual(len(list(parts)), 1)
def test_get_active(self): def test_get_active(self):
@ -35,6 +35,12 @@ class SystemPartTest(unittest.TestCase):
parts[0].detach(self.database) parts[0].detach(self.database)
self.assertEqual(len(list(SystemPart.get_active(self.database))), 0) 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): def test_attach_detach(self):
parts = list(SystemPart.get_active(self.database)) parts = list(SystemPart.get_active(self.database))
self.assertEqual(len(parts), 1) self.assertEqual(len(parts), 1)
@ -49,7 +55,7 @@ class SystemPartTest(unittest.TestCase):
self.assertEqual(len(list(SystemPart.get_active(self.database))), 0) self.assertEqual(len(list(SystemPart.get_active(self.database))), 0)
def test_freeze(self): def test_freeze(self):
parts = list(SystemPart.all(self.database)) parts = list(SystemPart.get(self.database))
# There can be other backups in the folder # There can be other backups in the folder
prev_backups = set(self._get_backups()) prev_backups = set(self._get_backups())
parts[0].freeze(self.database) parts[0].freeze(self.database)