sillyorm package¶
Submodules¶
sillyorm.registry module¶
- class sillyorm.registry.Registry(create_engine_url: str, create_engine_kwargs: dict[str, ~typing.Any] | None=None, environment_class: type[Environment] = <class 'sillyorm.environment.Environment'>)¶
Bases:
objectsillyORM Model Registry - keeps track of models and handles model inheritance
- Variables:
engine – The SQLAlchemy database engine
metadata – The SQLAlchemy MetaData
- Parameters:
create_engine_url (str) – The URL passed to sqlalchemy.create_engine
create_engine_kwargs (dict[str, Any]) – keyword arguments passed to sqlalchemy.create_engine
- environment(autocommit: bool = False) Generator[Environment, None, None]¶
Context manager for environments, will close the environment for you when you are done.
- Parameters:
autocommit (bool, optional) – Whether to automatically run commit after each database transaction that requires it (and rollback on error)
- get_environment(autocommit: bool = False) Environment¶
Returns a new environment object (will also grab a new connection from the connection pool)
- Parameters:
autocommit (bool, optional) – Whether to automatically run commit after each database transaction that requires it (and rollback on error)
- Returns:
The new Environment object
- Return type:
- get_schema_diffs() Any¶
Asks Alembic for the differences between the metadata in the registry and the current DB state are
- init_db_tables(automigrate: Literal['ignore', 'none', 'safe', 'auto'] = 'safe', auto_create: bool = True) None¶
Initializes database tables.
automigrate modes: - ignore: ignore the state of the DB schema and just do nothing - none: do not run any migrations, error if the DB schema doesn’t match - safe: only do safe migrations (generally used together with auto_create) - auto: automatically generate and run do all migrations - may cause data loss
- register_model(model: type[Model]) None¶
Registers a model class in the Registry (for later initialization)
The order in which this function is called on each model matters! It determines the exact inheritance!
- reset() None¶
Reset the registry object, minus the models that have been registered
- reset_full() None¶
Fully Reset the registry object, including the models that have been registered
- resolve_tables() None¶
Resolve model inheritance and build the models table in the registry
sillyorm.environment module¶
- class sillyorm.environment.Environment(models: dict[str, type[Model]], connection: sqlalchemy.Connection, registry: Registry, autocommit: bool = False)¶
Bases:
objectThis class is meant for keeping track of
modelsregistered in it, some settings and the database connection (SQLAlchemy Connection object).Once registered, models in the environment can be accessed using the index operator. When a model is accessed this way, it will return an empty recordset.
it is not meant to be initialized directly by the user
- Variables:
connection (sqlalchemy.Connection) – The database Connection
registry (
sillyorm.registry.Registry) – The registry this environment object was created fromautocommit (bool) – Whether to automatically run commit after each database transaction that requires it (and rollback on error)
- Parameters:
models – The database cursor that will be passed to all models
connection (sqlalchemy.Connection) – The database connection
registry (
sillyorm.registry.Registry) – The registry this environment object was created fromautocommit (bool, optional) – Whether to automatically run commit after each database transaction that requires it (and rollback on error)
- clone() Self¶
Clone the environment - the connection between the two will be shared
- close() None¶
Close this environment object, close it’s connection If it has an active transaction that will be rolled back.
- managed_transaction() Generator[None, None, None]¶
Context manager for transactions, this is mostly for internal use and will do not do _anything_ without autocommit being set!
- transaction() Generator[None, None, None]¶
Context manager for transactions, this will start a transaction and roll it back on error
sillyorm.exceptions module¶
- exception sillyorm.exceptions.SillyORMException¶
Bases:
ExceptionGeneric sillyORM Exceptions
sillyorm.fields module¶
- class sillyorm.fields.Boolean(required: bool = False, unique: bool = False, default: Any = None)¶
Bases:
FieldBoolean field. Can represent either True or False.
class ExampleModel(sillyorm.model.Model): _name = "example_bool" field = sillyorm.fields.Boolean() env = reinit_env([ExampleModel]) record = env["example_bool"].create({"field": True}) print(record.field) record.field = False print(record.field) record.field = None print(record.field)
True False None
- sql_type: TypeEngine = Boolean()¶
- class sillyorm.fields.Date(required: bool = False, unique: bool = False, default: Any = None)¶
Bases:
FieldDate field. Represents a python date object.
import datetime class ExampleModel(sillyorm.model.Model): _name = "example3" field = sillyorm.fields.Date() env = reinit_env([ExampleModel]) record = env["example3"].create({"field": datetime.date(1970, 1, 1)}) print(record.field) record.field += datetime.timedelta(days=1) print(record.field) record.field = None print(record.field)
1970-01-01 1970-01-02 None
- sql_type: TypeEngine = Date()¶
- class sillyorm.fields.Datetime(tzinfo: tzinfo | None, convert_tz: bool = False, required: bool = False, unique: bool = False, default: Any = None)¶
Bases:
FieldDatetime field. Represents a python datetime object.
A timezone (or the value None - which means it’s naive) must be provided because in the database this field may not store any timzeone-related information. Mixing timezones would be fatal so this field takes care of that for you.
- Parameters:
tzinfo (datetime.tzinfo | None) – time zone of the date stored - None means it’s a naive datetime object
convert_tz (bool) – if the datetime should be converted to the supplied tzinfo if possible (e.g. tzinfo set to UTC and you can supply tz=EST and it’ll be converted using astimezone)
import datetime class ExampleModel(sillyorm.model.Model): _name = "example_datetime" field = sillyorm.fields.Datetime(None) env = reinit_env([ExampleModel]) record = env["example_datetime"].create({"field": datetime.datetime(1970, 1, 1, 1, 2, 3)}) print(record.field) record.field += datetime.timedelta(days=1, hours=2, minutes=6) print(record.field) record.field = None print(record.field)
1970-01-01 01:02:03 1970-01-02 03:08:03 None
- sql_type: TypeEngine = DateTime()¶
- class sillyorm.fields.Field(required: bool = False, unique: bool = False, default: Any = None)¶
Bases:
objectBase descriptor class for
BaseModelfields- Variables:
sql_type (
sqlalchemy.types.TypeEngine) – SQL type of the fieldmaterialize (bool) – Whether the field actually exists as a column in the database table
constraints (list[sqlalchemy.schema.Constraint | tuple[str, Any]]) – SQL constraints of the field
name (str) – column name of the field
required (bool) – If the field must be set (checked via SQL constraints and runtime checks)
unique (bool) – If the field’s value should be unique in the column (checked via SQL constraints)
default (Any) – The constant default value for a column inserted during record creation - equivalent to SQLAlchemy Column default=
- Parameters:
required (bool) – If the field must be set (checked via SQL constraints and runtime checks)
unique (bool) – If the field’s value should be unique in the column (checked via SQL constraints)
default (Any) – The constant default value for a column inserted during record creation - equivalent to SQLAlchemy Column default=
- Default required:
False
- Default unique:
False
- Default default:
None
- constraints: list[SchemaItem | tuple[str, Any]]¶
- materialize = True¶
- name: str = None¶
- sql_type: TypeEngine = None¶
- class sillyorm.fields.Float(required: bool = False, unique: bool = False, default: Any = None)¶
Bases:
FieldFloat field. Can represent floating point numbers from at least
-1.2e-38to3.4e+38(may be significantly more depending on the dbms used).class ExampleModel(sillyorm.model.Model): _name = "example_float" field = sillyorm.fields.Float() env = reinit_env([ExampleModel]) record = env["example_float"].create({"field": 32768.123321}) print(record.field) record.field = -0.000000000000000000000000000000000000012 print(record.field) record.field = 340000000000000000000000000000000000000.0 print(record.field) record.field = 123 print(record.field) record.field = None print(record.field)
32768.123321 -1.2e-38 3.4e+38 123.0 None
- sql_type: TypeEngine = Float()¶
- class sillyorm.fields.Id(required: bool = False, unique: bool = False, default: Any = None)¶
Bases:
IntegerSpecial ID field used as PRIMARY KEY in model tables. It’s value cannot be changed.
class ExampleModel(sillyorm.model.Model): _name = "example1" # Each model automatically has an ID field env = reinit_env([ExampleModel]) record = env["example1"].create({}) record2 = env["example1"].create({}) print(record.id) print(record2.id)
1 2
- class sillyorm.fields.Integer(required: bool = False, unique: bool = False, default: Any = None)¶
Bases:
FieldInteger field. Can represent numbers from at least
-32768to32767(may be significantly more depending on the dbms used).class ExampleModel(sillyorm.model.Model): _name = "example0" field = sillyorm.fields.Integer() env = reinit_env([ExampleModel]) record = env["example0"].create({"field": 5}) print(record.field) record.field = -32768 print(record.field) record.field = 32767 print(record.field) record.field = None print(record.field)
5 -32768 32767 None
- sql_type: TypeEngine = Integer()¶
- class sillyorm.fields.Many2many(foreign_model: str, join_table_name: str = '', join_table_self_name: str = '', join_table_foreign_name: str = '')¶
Bases:
FieldMany to many relational field.
Warning
This field’s implementation is currently incomplete
- materialize = False¶
- class sillyorm.fields.Many2one(foreign_model: str, ondelete: Literal['set null', 'restrict', 'cascade'] = 'restrict', required: bool = False, unique: bool = False, default: Any = None)¶
Bases:
IntegerMany to one relational field. Represents a single record of another model.
When read this field returns a recordset. When written it expects an integer (the ID of a foreign record).
class ExampleModel1(sillyorm.model.Model): _name = "example4" field = sillyorm.fields.String() class ExampleModel2(sillyorm.model.Model): _name = "example5" many2one_field = sillyorm.fields.Many2one("example4") env = reinit_env([ExampleModel1, ExampleModel2]) other_record = env["example4"].create({"field": "Hello world!"}) record = env["example5"].create({"many2one_field": other_record.id}) print(other_record.field) print(record.many2one_field) print(record.many2one_field.field) record.many2one_field.field = "test" print(other_record.field) record.many2one_field = None print(record.many2one_field)
Hello world! example4[1] Hello world! test None
- Parameters:
foreign_model (str) – Foreign model name
ondelete (Literal["set null", "restrict", "cascade"]) – What to do when the related record is deleted set null: set this relation to null and continue restrict: prevent the related record from being deleted cascade: delete this record too
- class sillyorm.fields.Many2xCommand¶
Bases:
objectCommands for One2many and Many2many fields
- LINK = 1¶
- UNLINK = 2¶
- class sillyorm.fields.One2many(foreign_model: str, foreign_field: str, required: bool = False, unique: bool = False, default: Any = None)¶
Bases:
FieldOne to many relational field. It’s the inverse of a
Many2onefield. Represents multiple records of another model. This field does not exist in the database table.When read this field returns a recordset. It cannot be written.
class ExampleModel1(sillyorm.model.Model): _name = "example6" field = sillyorm.fields.String() one2many_field = sillyorm.fields.One2many("example7", "many2one_field") class ExampleModel2(sillyorm.model.Model): _name = "example7" many2one_field = sillyorm.fields.Many2one("example6") env = reinit_env([ExampleModel1, ExampleModel2]) other_record = env["example6"].create({}) record = env["example7"].create({"many2one_field": other_record.id}) record2 = env["example7"].create({"many2one_field": other_record.id}) print(record.many2one_field) print(record2.many2one_field) print(other_record.one2many_field)
example6[1] example6[1] example7[1, 2]
- Parameters:
foreign_model (str) – Foreign model name
foreign_field (str) – Foreign
Many2onefield name
- materialize = False¶
- class sillyorm.fields.Selection(options: list[str], length: int = 255, required: bool = False, unique: bool = False, default: Any = None)¶
Bases:
StringSelection field. Basically just a string field with a little logic around it that allows you to choose between multiple different predefined options.
class ExampleModel(sillyorm.model.Model): _name = "example_selection" field = sillyorm.fields.Selection(["option1", "option2"]) env = reinit_env([ExampleModel]) record = env["example_selection"].create({"field": "option1"}) print(record.field) record.field = "option2" print(record.field) record.field = None print(record.field)
option1 option2 None
- Parameters:
options (list[str]) – List of possible selection options
length (int, optional) – Maximum selection length, defaults to 255
- class sillyorm.fields.String(length: int = 255, required: bool = False, unique: bool = False, default: Any = None)¶
Bases:
FieldString field. Represents a string of at most
lengthcharactersclass ExampleModel(sillyorm.model.Model): _name = "example2" field = sillyorm.fields.String() env = reinit_env([ExampleModel]) record = env["example2"].create({"field": "hello"}) print(record.field) record.field += " world!" print(record.field) record.field = None print(record.field)
hello hello world! None
- Parameters:
length (int, optional) – Maximum string length, defaults to 255
- class sillyorm.fields.Text(required: bool = False, unique: bool = False, default: Any = None)¶
Bases:
FieldText field. Represents a large string of text
class ExampleModel(sillyorm.model.Model): _name = "example_text" field = sillyorm.fields.Text() env = reinit_env([ExampleModel]) record = env["example_text"].create({"field": "hello"}) print(record.field) record.field += " world!" print(record.field) largestring = "0123456789" * 100000 # 1MB of data record.field = largestring print(record.field == largestring) record.field = None print(record.field)
hello hello world! True None
sillyorm.model module¶
- class sillyorm.model.AbstractModel(env: Environment, ids: list[int])¶
Bases:
BaseModelUse this as the base for any Abstract Models. Won’t create a database table. See
sillyorm.model.BaseModelfor docs
- class sillyorm.model.BaseModel(env: Environment, ids: list[int])¶
Bases:
objectEach model represents a single table in the database. A model can have fields which represent columns in the database table.
The _name attribute specifies the name of the database table the model represents and the name of the model in the environment.
An instance of the model class (or a subclass instance) represents a recordset.
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.
class ExampleModel(sillyorm.model.Model): _name = "example0" field = sillyorm.fields.String() registry.register_model(ExampleModel) registry.resolve_tables() registry.init_db_tables() env = registry.get_environment() record = env["example0"].create({"field": "Hello world!"}) print(record.field)
Hello world!
- Variables:
env (
sillyorm.environment.Environment) – The environment- Parameters:
env (list[int]) – The environment
ids – list of id’s the recordset should have
- __getitem__(key: int) Self¶
- __iter__() Iterator[Self]¶
- browse(ids: list[int] | int) None | Self¶
Returns a recordset for the ids provided.
Warning
The order of the ids in the recordset returned may not be the same as the ids provided as input
- Parameters:
ids – The ids or id
- Returns:
A recordset with the ids provided. None if none of the ids could be found
- Return type:
None | Self
- create(vals: dict[str, Any]) Self¶
Creates a recordset with the values provided.
- Parameters:
vals (dict[str, Any]) – The values to write into the new recordset. The keys represent the field names and the values the values for the fields
- Returns:
The recordset that was created (containing one record)
- Return type:
Self
- delete() None¶
Deletes all records in the recordset
- ensure_one() Self¶
Makes sure the recordset contains exactly one record. Raises an exception otherwise
- Raises:
SillyORMException – If the recordset does not contain exactly one record
- id¶
Special
sillyorm.fields.Idfield used as PRIMARY KEY
- property ids: list[int]¶
Get all IDs of records in this recordset
- read(field_names: list[str]) list[dict[str, Any]]¶
Reads the specified fields of the recordset.
- Parameters:
field_names (list[str]) – The fields to read
- Returns:
The fields read as a list of dictionaries.
- Return type:
list[dict[str, Any]]
- search(domain: list[str | tuple[str, str, Any]], order_by: str | None = None, order_asc: bool = True, offset: int | None = None, limit: int | None = None) Self¶
Searches records.
Search domains are closely tied to the SQL WHERE statement.
[ "(", ("test2", "=", "test2"), "&", ("test", "=", "hello world!"), ")", "|", ("test2", "=", "2 Hii!!"), ]
This search domain will result in SQL code that looks something like this:
SELECT "id" FROM "test_model" WHERE ( "test2" = 'test2' AND "test" = 'hello world!' ) OR "test2" = '2 Hii!!';
Search operators:
= Equals to
in similar to = but checks if equal to one of the items in the list
!= not equal
> greater than
>= greater than or equal
< less than
<= less than or equal
=ilike matches against the pattern provided (case-insentitive), _ in the pattern matches any single character and % matches any string of zero or more characters
ilike similar to =ilike but will wrap the pattern provided in %
Usage example:
class ExampleModel(sillyorm.model.Model): _name = "example1" field = sillyorm.fields.String() registry.register_model(ExampleModel) registry.resolve_tables() registry.init_db_tables() env = registry.get_environment() record1 = env["example1"].create({"field": "test1"}) record2 = env["example1"].create({"field": "test2"}) record3 = env["example1"].create({"field": "test3"}) print(record1.id, record2.id, record3.id) print(env["example1"].search([ ("field", "=", "test1"), "|", ("field", "!=", "test2"), ]))
1 2 3 example1[1, 3]
- Parameters:
domain (list[str | tuple[str, str, Any]]) – The search domain.
order_by (str | None) – The column to order by
order_asc (bool) – Wether the order is ascending or not
offset (int | None) – The row offset to use
limit (int | None) – The maximum amount of rows to return
- Returns:
A recordset with the records found. An empty recordset if nothing could be found
- Return type:
Self
- search_count(domain: list[str | tuple[str, str, Any]]) int¶
Counts the total amount of records that match a domain.
The domain is the same format as for the search function.
Usage example:
class ExampleModel(sillyorm.model.Model): _name = "example_msc1" field = sillyorm.fields.String() registry.register_model(ExampleModel) registry.resolve_tables() registry.init_db_tables() env = registry.get_environment() record1 = env["example_msc1"].create({"field": "test1"}) record2 = env["example_msc1"].create({"field": "test1"}) record3 = env["example_msc1"].create({"field": "test2"}) print(env["example_msc1"].search_count([ ("field", "=", "test1"), ])) print(env["example_msc1"].search_count([ ("field", "=", "test2"), ]))
2 1
- Parameters:
domain (list[str | tuple[str, str, Any]]) – The search domain.
- Returns:
The amount of records that match the provided domain
- Return type:
int
- write(vals: dict[str, Any]) None¶
Writes the specified fields into all records contained in the recordset.
- Parameters:
vals (dict[str, Any]) – The values to write. The keys represent the field names and the values the values for the fields
- class sillyorm.model.Model(env: Environment, ids: list[int])¶
Bases:
BaseModelUse this as the base for any normal Models. Will create a database table. See
sillyorm.model.BaseModelfor docs
- sillyorm.model.constraints(*fields_list: str) Callable[[Callable[[BaseModel], None]], Callable[[BaseModel], None]]¶
Decorator for constraints.
Decorate a function in a model class with this, and that function will be called within create and write on that Model.
The arguments are the field names it should be called for (it will only be called if those fields change)
class ExampleModel(sillyorm.model.Model): _name = "example_c" field = sillyorm.fields.String() @sillyorm.model.constraints("field") def _verify_field(self): for record in self: if record.field == "invalid": raise Exception("invalid data!") registry.register_model(ExampleModel) registry.resolve_tables() registry.init_db_tables() env = registry.get_environment() record = env["example_c"].create({"field": "Hello world!"}) try: record.field = "invalid" except Exception as e: print(repr(e))
Exception('invalid data!')
sillyorm.migration_helpers module¶
- sillyorm.migration_helpers.helper_do_migrate(revision: str = 'head', downgrade: bool = False) None¶
Runs migration scripts up to revision
- sillyorm.migration_helpers.helper_gen_migrations(message: str | Callable[[], str], revid: str | None = None, head: str = 'head') bool¶
Checks for difference of the Registry metadata to the DB autogenerates a migration script, returns True if a migration was generated, False otherwise
- sillyorm.migration_helpers.helper_init(registry: Registry, migration_folder_path: str, script_template_path: str | None = None, version_table: str = 'alembic_version') None¶
Initialize the migration helper. This must be called before using any of the helper functions!
Will also initialize the migration folder in case it isn’t initialized yet