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"
env.register_model(ExampleModel)
env.init_tables()
When a model is registered the ORM ensures the table with all required fields is created. If any columns/fields exist in the database but are not specified in the model they will be removed in the database.
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()
env.register_model(ExampleModel)
env.register_model(ExampleModelCopy)
env.init_tables()
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()
env.register_model(ExampleModel)
env.register_model(ExampleModelExtension)
env.init_tables()
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()
env.register_model(ExampleModel)
env.register_model(ExampleModelCopy)
env.init_tables()
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()
env.register_model(ExampleModelSomefield)
env.register_model(ExampleModel)
env.register_model(ExampleModelCopy)
env.init_tables()
env["example_orm_ext_inheritance"].create({}).somefield
env["example_orm_ext_inheritance"].create({}).field1
env["example_orm_ext_inheritance"].create({}).field2
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 cursor can be accessed from the environment
# the database cursor can be accessed from the environment
>>> type(env.cr)
<class 'sillyorm.dbms.sqlite.SQLiteCursor'>
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 integerFloat
represents a floating point numberString
represents a stringText
represents a large stringDate
represents a Date (as datetime.date)Datetime
represents a Datetime (as datetime.datetime)Boolean
represents a BooleanSelection
represents a SelectionMany2one
represents a many to one relationshipOne2many
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()
env.register_model(ExampleModel)
env.init_tables()
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}")
env.register_model(ExampleModel)
env.init_tables()
record = env["example2"].create({"name": "test"})
record.somefunc()
example2[1]
it: example2[1]