Basic concepts

Models

Each model represents a single table in the database. A model can have fields which represent columns in the database table.

A model is a class that inherits from sillyorm.model.Model. It has a _name attribute which specifies the name of the database table and the name of the model in the environment.

class ExampleModel(sillyorm.model.Model):
    _name = "example0"

registry.register_model(ExampleModel)
registry.resolve_tables()
registry.init_db_tables()
env = registry.get_environment()

Warning

You should never call the constructor of the model class yourself. Get an empty recordset via the environment and interact with the model from there.

A model can also be inherited and extended

Standard Python Inheritance:

class ExampleModel(sillyorm.model.Model):
    _name = "example_inheritance"
    field1 = sillyorm.fields.Integer()

class ExampleModelCopy(ExampleModel):
    _name = "example_inheritance_copy"
    field2 = sillyorm.fields.String()

registry.register_model(ExampleModel)
registry.register_model(ExampleModelCopy)
registry.resolve_tables()
registry.init_db_tables()
env = registry.get_environment()
env["example_inheritance"].create({}).field1
env["example_inheritance_copy"].create({}).field1
env["example_inheritance_copy"].create({}).field2

This will cause all fields to be copied on the inherited model. If a field is defined in both the base class and the inherited one the inherited one will be put into the database. At the moment it is not possible to remove a field from an inherited model

Extension:

class ExampleModel(sillyorm.model.Model):
    _name = "example_extension"
    field1 = sillyorm.fields.Integer()
    field2 = sillyorm.fields.Integer()

class ExampleModelExtension(sillyorm.model.Model):
    _name = "example_extension"
    _extends = "example_extension"
    # overrides field2 on original model, now field2 is a String
    field2 = sillyorm.fields.String()
    # adds a new field to the original model
    field3 = sillyorm.fields.String()

registry.register_model(ExampleModel)
registry.register_model(ExampleModelExtension)
registry.resolve_tables()
registry.init_db_tables()
env = registry.get_environment()
env["example_extension"].create({}).field1
env["example_extension"].create({}).field2
env["example_extension"].create({}).field3

This will add fields/modify fields on the original model. At the moment it is not possible to remove a field from an extended model

Inheritance (via ORM):

class ExampleModel(sillyorm.model.Model):
    _name = "example_orm_inheritance"
    field1 = sillyorm.fields.Integer()

class ExampleModelCopy(sillyorm.model.Model):
    _name = "example_orm_inheritance_copy"
    _inherits = ["example_orm_inheritance"] # order matters here (later in array has higher priority)
    field2 = sillyorm.fields.String()

registry.register_model(ExampleModel)
registry.register_model(ExampleModelCopy)
registry.resolve_tables()
registry.init_db_tables()
env = registry.get_environment()
env["example_orm_inheritance"].create({}).field1
env["example_orm_inheritance_copy"].create({}).field1
env["example_orm_inheritance_copy"].create({}).field2

This will cause all fields to be copied on the inherited model. If a field is defined in both the base class and the inherited one the inherited one will be put into the database. At the moment it is not possible to remove a field from an inherited model

Inheritance (via ORM) and extension may also be combined:

class ExampleModelSomefield(sillyorm.model.Model):
    _name = "example_orm_ext_inheritance_somefield"
    somefield = sillyorm.fields.Integer()

class ExampleModel(sillyorm.model.Model):
    _name = "example_orm_ext_inheritance"
    field1 = sillyorm.fields.Integer()

class ExampleModelCopy(sillyorm.model.Model):
    _name = "example_orm_ext_inheritance"
    _extends = "example_orm_ext_inheritance"
    _inherits = ["example_orm_ext_inheritance_somefield"] # order matters here (later in array has higher priority)
    field2 = sillyorm.fields.String()

registry.register_model(ExampleModelSomefield)
registry.register_model(ExampleModel)
registry.register_model(ExampleModelCopy)
registry.resolve_tables()
registry.init_db_tables()
env = registry.get_environment()
env["example_orm_ext_inheritance"].create({}).somefield
env["example_orm_ext_inheritance"].create({}).field1
env["example_orm_ext_inheritance"].create({}).field2

Registry

The Registry class keeps track of the database connection pool and Model classes.

You can create environments (kinda like DB cursors) from the registry.

>>> new_env = registry.get_environment(autocommit=True)
>>> type(new_env)
<class 'sillyorm.environment.Environment'>

Environment

The environment class keeps track of the database cursor and Models registered in the database.

You can get an empty recordset for each model registered in the environment

>>> env["example0"]
example0[]

The environment can be accessed from each recordset

# the environment can be accessed from each recordset
>>> type(env["example0"].env)
<class 'sillyorm.environment.Environment'>

The database connection can be accessed from the environment

# the database connection can be accessed from the environment
>>> type(env.connection)
<class 'sqlalchemy.engine.base.Connection'>

Fields

There are various kinds of fields. By default each model has a special id field which is the primary key.

Currently sillyORM supports the following fields:

  • Integer represents an integer

  • Float represents a floating point number

  • String represents a string

  • Text represents a large string

  • Date represents a Date (as datetime.date)

  • Datetime represents a Datetime (as datetime.datetime)

  • Boolean represents a Boolean

  • Selection represents a Selection

  • Many2one represents a many to one relationship

  • One2many represents a one to many relationship (requires a many to one on the other side)

Most fields support None as a value, and are initialized with None by default.

Fields are specified as class attributes on a child of the Model class. The attribute name specifies the column name in the database.

class ExampleModel(sillyorm.model.Model):
    _name = "example1"

    name = sillyorm.fields.String()
    test = sillyorm.fields.String()

registry.register_model(ExampleModel)
registry.resolve_tables()
registry.init_db_tables()
env = registry.get_environment()

Recordsets

An instance of a model class is a recordset. It may contain none to multiple records.

Recordsets can be empty

# empty recordset
>>> env["example1"]
example1[]

Recordsets can contain single records

# recordset with one record
>>> rec_1 = env["example1"].create({"name": "this is record 1"})
>>> rec_1
example1[1]
>>> rec_1.name
'this is record 1'
>>> rec_1.id
1

# another recordset with one record
>>> env["example1"].create({"name": "this is record 2"})
example1[2]

Recordsets can contain multiple records

# recordset with two records
>>> rec_12 = env["example1"].browse([1, 2])
>>> rec_12
example1[1, 2]
>>> rec_12.name  # reading of a field is only possible if the recordset contains exactly one record
Traceback (most recent call last):
...
sillyorm.exceptions.SillyORMException: ensure_one found 2 id's
>>> rec_12.read(["name"])  # if a recordset with multiple records has to be read use the `read` method
[{'name': 'this is record 1'}, {'name': 'this is record 2'}]

Recordsets can be iterated over

>>> rec_12 = env["example1"].browse([1, 2])
>>> for record in rec_12: record
example1[1]
example1[2]

Recordsets can be subscripted

>>> rec_12 = env["example1"].browse([1, 2])
>>> rec_12[0]
example1[1]
>>> rec_12[1]
example1[2]

There is a function to ensure a recordset contains exactly one record. It will raise an exception if that isn’t the case

>>> rec_1 = env["example1"].browse(1)
>>> rec_1.ensure_one()
example1[1]

Fields can have no value

# recordset with one record
>>> rec_3 = env["example1"].create({"name": "this is record 3"})
>>> rec_3
example1[3]
>>> repr(rec_3.test)
'None'
>>> rec_3.test = "test"
>>> rec_3.test
'test'
>>> rec_3.test = None  # setting a field to None is also possible
>>> repr(rec_3.test)
'None'

Model Functions

A model can have functions

class ExampleModel(sillyorm.model.Model):
    _name = "example2"

    name = sillyorm.fields.String()

    def somefunc(self):
        print(self)
        for record in self:
            print(f"it: {self}")

registry.register_model(ExampleModel)
registry.resolve_tables()
registry.init_db_tables()
env = registry.get_environment()
record = env["example2"].create({"name": "test"})
record.somefunc()
example2[1]
it: example2[1]