mirror of
https://github.com/Infinidat/infi.clickhouse_orm.git
synced 2024-11-25 10:13:45 +03:00
documentation
This commit is contained in:
parent
27ee24843a
commit
64f8cde1c0
58
README.rst
58
README.rst
|
@ -32,9 +32,9 @@ Models are defined in a way reminiscent of Django's ORM:
|
||||||
|
|
||||||
engine = engines.MergeTree('birthday', ('first_name', 'last_name', 'birthday'))
|
engine = engines.MergeTree('birthday', ('first_name', 'last_name', 'birthday'))
|
||||||
|
|
||||||
It is possible to provide a default value for a field, instead of it's "natural" default (empty string for string fields, zero for numeric fields etc.).
|
It is possible to provide a default value for a field, instead of its "natural" default (empty string for string fields, zero for numeric fields etc.).
|
||||||
|
|
||||||
See below for the supported model field types.
|
See below for the supported field types and table engines.
|
||||||
|
|
||||||
Using Models
|
Using Models
|
||||||
------------
|
------------
|
||||||
|
@ -48,8 +48,8 @@ Once you have a model, you can create model instances:
|
||||||
>>> dan.first_name
|
>>> dan.first_name
|
||||||
u'Dan'
|
u'Dan'
|
||||||
|
|
||||||
When values are assigned to a model fields, they are immediately converted to their Pythonic data type.
|
When values are assigned to model fields, they are immediately converted to their Pythonic data type.
|
||||||
In case the value is invalid, a ValueError is raised:
|
In case the value is invalid, a ``ValueError`` is raised:
|
||||||
|
|
||||||
.. code:: python
|
.. code:: python
|
||||||
|
|
||||||
|
@ -61,7 +61,10 @@ In case the value is invalid, a ValueError is raised:
|
||||||
>>> suzy.birthday = '1922-05-31'
|
>>> suzy.birthday = '1922-05-31'
|
||||||
ValueError: DateField out of range - 1922-05-31 is not between 1970-01-01 and 2038-01-19
|
ValueError: DateField out of range - 1922-05-31 is not between 1970-01-01 and 2038-01-19
|
||||||
|
|
||||||
To write your instances to ClickHouse, you need a Database instance:
|
Inserting to the Database
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
To write your instances to ClickHouse, you need a ``Database`` instance:
|
||||||
|
|
||||||
.. code:: python
|
.. code:: python
|
||||||
|
|
||||||
|
@ -76,16 +79,53 @@ If necessary, you can specify a different database URL and optional credentials:
|
||||||
|
|
||||||
db = Database('my_test_db', db_url='http://192.168.1.1:8050', username='scott', password='tiger')
|
db = Database('my_test_db', db_url='http://192.168.1.1:8050', username='scott', password='tiger')
|
||||||
|
|
||||||
Using the Database instance you can create a table for your model, and insert instances to it:
|
Using the ``Database`` instance you can create a table for your model, and insert instances to it:
|
||||||
|
|
||||||
.. code:: python
|
.. code:: python
|
||||||
|
|
||||||
db.create_table(Person)
|
db.create_table(Person)
|
||||||
db.insert([dan, suzy])
|
db.insert([dan, suzy])
|
||||||
|
|
||||||
The insert method can take any iterable of model instances, but they all must belong to the same model class.
|
The ``insert`` method can take any iterable of model instances, but they all must belong to the same model class.
|
||||||
|
|
||||||
|
Reading from the Database
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
Loading model instances from the database is simple:
|
||||||
|
|
||||||
|
.. code:: python
|
||||||
|
|
||||||
|
for person in db.select("SELECT * FROM my_test_db.person", model_class=Person):
|
||||||
|
print person.first_name, person.last_name
|
||||||
|
|
||||||
|
Do not include a ``FORMAT`` clause in the query, since the ORM automatically sets the format to ``TabSeparatedWithNamesAndTypes``.
|
||||||
|
|
||||||
|
It is possible to select only a subset of the columns, and the rest will receive their default values:
|
||||||
|
|
||||||
|
.. code:: python
|
||||||
|
|
||||||
|
for person in db.select("SELECT first_name FROM my_test_db.person WHERE last_name='Smith'", model_class=Person):
|
||||||
|
print person.first_name
|
||||||
|
|
||||||
|
Specifying a model class is not required. In case you do not provide a model class, an ad-hoc class will
|
||||||
|
be defined based on the column names and types returned by the query:
|
||||||
|
|
||||||
|
.. code:: python
|
||||||
|
|
||||||
|
for row in db.select("SELECT max(height) as max_height FROM my_test_db.person"):
|
||||||
|
print row.max_height
|
||||||
|
|
||||||
|
Counting
|
||||||
|
--------
|
||||||
|
|
||||||
|
The ``Database`` class also supports counting records easily:
|
||||||
|
|
||||||
|
.. code:: python
|
||||||
|
|
||||||
|
>>> db.count(Person)
|
||||||
|
117
|
||||||
|
>>> db.count(Person, conditions="height > 1.90")
|
||||||
|
6
|
||||||
|
|
||||||
Field Types
|
Field Types
|
||||||
-----------
|
-----------
|
||||||
|
@ -106,6 +146,10 @@ Currently the following field types are supported:
|
||||||
- DateField
|
- DateField
|
||||||
- DateTimeField
|
- DateTimeField
|
||||||
|
|
||||||
|
Table Engines
|
||||||
|
-------------
|
||||||
|
|
||||||
|
TBD
|
||||||
|
|
||||||
|
|
||||||
Development
|
Development
|
||||||
|
|
|
@ -17,6 +17,7 @@ class Database(object):
|
||||||
self._send('CREATE DATABASE IF NOT EXISTS ' + db_name)
|
self._send('CREATE DATABASE IF NOT EXISTS ' + db_name)
|
||||||
|
|
||||||
def create_table(self, model_class):
|
def create_table(self, model_class):
|
||||||
|
# TODO check that model has an engine
|
||||||
self._send(model_class.create_table_sql(self.db_name))
|
self._send(model_class.create_table_sql(self.db_name))
|
||||||
|
|
||||||
def drop_table(self, model_class):
|
def drop_table(self, model_class):
|
||||||
|
|
|
@ -15,23 +15,30 @@ class Field(object):
|
||||||
self.default = default or self.class_default
|
self.default = default or self.class_default
|
||||||
|
|
||||||
def to_python(self, value):
|
def to_python(self, value):
|
||||||
"""
|
'''
|
||||||
Converts the input value into the expected Python data type, raising ValueError if the
|
Converts the input value into the expected Python data type, raising ValueError if the
|
||||||
data can't be converted. Returns the converted value. Subclasses should override this.
|
data can't be converted. Returns the converted value. Subclasses should override this.
|
||||||
"""
|
'''
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def validate(self, value):
|
def validate(self, value):
|
||||||
|
'''
|
||||||
|
Called after to_python to validate that the value is suitable for the field's database type.
|
||||||
|
Subclasses should override this.
|
||||||
|
'''
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def _range_check(self, value, min_value, max_value):
|
def _range_check(self, value, min_value, max_value):
|
||||||
|
'''
|
||||||
|
Utility method to check that the given value is between min_value and max_value.
|
||||||
|
'''
|
||||||
if value < min_value or value > max_value:
|
if value < min_value or value > max_value:
|
||||||
raise ValueError('%s out of range - %s is not between %s and %s' % (self.__class__.__name__, value, min_value, max_value))
|
raise ValueError('%s out of range - %s is not between %s and %s' % (self.__class__.__name__, value, min_value, max_value))
|
||||||
|
|
||||||
def get_db_prep_value(self, value):
|
def get_db_prep_value(self, value):
|
||||||
"""
|
'''
|
||||||
Returns the field's value prepared for interacting with the database.
|
Returns the field's value prepared for interacting with the database.
|
||||||
"""
|
'''
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -58,6 +58,10 @@ class Model(object):
|
||||||
setattr(self, name, field.default)
|
setattr(self, name, field.default)
|
||||||
|
|
||||||
def __setattr__(self, name, value):
|
def __setattr__(self, name, value):
|
||||||
|
'''
|
||||||
|
When setting a field value, converts the value to its Pythonic type and validates it.
|
||||||
|
This may raise a ValueError.
|
||||||
|
'''
|
||||||
field = self.get_field(name)
|
field = self.get_field(name)
|
||||||
if field:
|
if field:
|
||||||
value = field.to_python(value)
|
value = field.to_python(value)
|
||||||
|
@ -65,15 +69,24 @@ class Model(object):
|
||||||
super(Model, self).__setattr__(name, value)
|
super(Model, self).__setattr__(name, value)
|
||||||
|
|
||||||
def get_field(self, name):
|
def get_field(self, name):
|
||||||
|
'''
|
||||||
|
Get a Field instance given its name, or None if not found.
|
||||||
|
'''
|
||||||
field = getattr(self.__class__, name, None)
|
field = getattr(self.__class__, name, None)
|
||||||
return field if isinstance(field, Field) else None
|
return field if isinstance(field, Field) else None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def table_name(cls):
|
def table_name(cls):
|
||||||
|
'''
|
||||||
|
Returns the model's database table name.
|
||||||
|
'''
|
||||||
return cls.__name__.lower()
|
return cls.__name__.lower()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_table_sql(cls, db_name):
|
def create_table_sql(cls, db_name):
|
||||||
|
'''
|
||||||
|
Returns the SQL command for creating a table for this model.
|
||||||
|
'''
|
||||||
parts = ['CREATE TABLE IF NOT EXISTS %s.%s (' % (db_name, cls.table_name())]
|
parts = ['CREATE TABLE IF NOT EXISTS %s.%s (' % (db_name, cls.table_name())]
|
||||||
cols = []
|
cols = []
|
||||||
for name, field in cls._fields:
|
for name, field in cls._fields:
|
||||||
|
@ -86,6 +99,9 @@ class Model(object):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def drop_table_sql(cls, db_name):
|
def drop_table_sql(cls, db_name):
|
||||||
|
'''
|
||||||
|
Returns the SQL command for deleting this model's table.
|
||||||
|
'''
|
||||||
return 'DROP TABLE IF EXISTS %s.%s' % (db_name, cls.table_name())
|
return 'DROP TABLE IF EXISTS %s.%s' % (db_name, cls.table_name())
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
Loading…
Reference in New Issue
Block a user