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
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
-----------------

View File

@ -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):

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
'''
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]

View File

@ -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)

View File

@ -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)