sillyorm package

Subpackages

Submodules

sillyorm.environment module

class sillyorm.environment.Environment(cursor: Cursor, do_commit: bool = True, update_tables: bool = True)

Bases: object

This class is meant for keeping track of models registered in it, some settings and the database cursor.

A model can be registered in the environment using the register_model function.

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.

>>> import tempfile
>>> import sillyorm
>>> class TestModel(sillyorm.model.Model):
...     _name = "testmodel"
>>> env = sillyorm.Environment(
...     sillyorm.dbms.sqlite.SQLiteConnection(
...         tempfile.NamedTemporaryFile().name
...     ).cursor()
... )
>>> env.register_model(TestModel)
>>> env.init_tables()
>>> env["testmodel"]
testmodel[]
Variables:
  • cr (sillyorm.sql.Cursor) – The database cursor

  • do_commit (bool) – Whether to run commit after each database transaction that requires it

  • update_tables (bool) – Whether to update database tables.

Parameters:
  • cursor (sillyorm.sql.Cursor) – The database cursor that will be passed to all models

  • do_commit (bool, optional) – Whether to run commit after each database transaction that requires it

  • update_tables

    Whether to automagically update database tables. This can be dangerous in some cases, e.g. when a field is renamed or field parameters are changed, all data in the field may be lost!

    When this is False and the tables don’t match an error will be thrown upon calling Environment.init_tables

__getitem__(key: str) Model
init_tables() None

Initializes database tables of all models registered in the environment and takes care of model inheritance

managed_transaction() Generator[None, None, None]

Context manager for transactions, only actually does anything when do_commit is set on the environment object

register_model(model: type[Model]) None

Registers a model class in the environment

Parameters:

model (type[Model]) – The Model to register

sillyorm.exceptions module

exception sillyorm.exceptions.SillyORMException

Bases: Exception

Generic sillyORM Exceptions

sillyorm.fields module

class sillyorm.fields.Boolean(required: bool = False, unique: bool = False)

Bases: Field

Boolean field. Can represent either True or False.

class ExampleModel(sillyorm.model.Model):
    _name = "example_bool"
    field = sillyorm.fields.Boolean()

env.register_model(ExampleModel)
env.init_tables()

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: SqlType = <SqlType BOOLEAN>
class sillyorm.fields.Date(required: bool = False, unique: bool = False)

Bases: Field

Date field. Represents a python date object.

import datetime

class ExampleModel(sillyorm.model.Model):
    _name = "example3"
    field = sillyorm.fields.Date()

env.register_model(ExampleModel)
env.init_tables()

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: SqlType = <SqlType DATE>
class sillyorm.fields.Datetime(tzinfo: tzinfo | None, required: bool = False, unique: bool = False)

Bases: Field

Datetime field. Represents a python datetime object.

A timezone (or none at all - which means it’s naive) must be provided because in the database this field does 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

import datetime

class ExampleModel(sillyorm.model.Model):
    _name = "example_datetime"
    field = sillyorm.fields.Datetime(None)

env.register_model(ExampleModel)
env.init_tables()

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: SqlType = <SqlType TIMESTAMP>
class sillyorm.fields.Field(required: bool = False, unique: bool = False)

Bases: object

Base descriptor class for Model fields

Variables:
  • sql_type (sillyorm.sql.SqlType) – SQL type of the field

  • materialize (bool) – Whether the field actually exists as a column in the database table

  • constraints (list[sillyorm.sql.SqlConstraint]) – 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)

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

False

Default unique:

False

materialize = True
model_post_init(record: Model) None

Called by the Model after the table is initialized

Parameters:

record (Model) – The Model the field is in

name: str = None
sql_type: SqlType = None
class sillyorm.fields.Float(required: bool = False, unique: bool = False)

Bases: Field

Float field. Can represent floating point numbers from at least -1.2e-38 to 3.4e+38 (may be significantly more depending on the dbms used).

class ExampleModel(sillyorm.model.Model):
    _name = "example_float"
    field = sillyorm.fields.Float()

env.register_model(ExampleModel)
env.init_tables()

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 = None
print(record.field)
32768.123321
-1.2e-38
3.4e+38
None
sql_type: SqlType = <SqlType FLOAT>
class sillyorm.fields.Id(required: bool = False, unique: bool = False)

Bases: Integer

Special 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.register_model(ExampleModel)
env.init_tables()

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)

Bases: Field

Integer field. Can represent numbers from at least -32768 to 32767 (may be significantly more depending on the dbms used).

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

env.register_model(ExampleModel)
env.init_tables()

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: SqlType = <SqlType INTEGER>
class sillyorm.fields.Many2one(foreign_model: str, required: bool = False, unique: bool = False)

Bases: Integer

Many 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.register_model(ExampleModel1)
env.register_model(ExampleModel2)
env.init_tables()

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

class sillyorm.fields.One2many(foreign_model: str, foreign_field: str, required: bool = False, unique: bool = False)

Bases: Field

One to many relational field. It’s the inverse of a Many2one field. 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.register_model(ExampleModel1)
env.register_model(ExampleModel2)
env.init_tables()

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 Many2one field name

materialize = False
class sillyorm.fields.Selection(options: list[str], length: int = 255, required: bool = False, unique: bool = False)

Bases: String

Selection 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.register_model(ExampleModel)
env.init_tables()

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)

Bases: Field

String field. Represents a string of at most length characters

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

env.register_model(ExampleModel)
env.init_tables()

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)

Bases: Field

Text field. Represents a large string of text

class ExampleModel(sillyorm.model.Model):
    _name = "example_text"
    field = sillyorm.fields.Text()

env.register_model(ExampleModel)
env.init_tables()

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.Model(env: Environment, ids: list[int])

Bases: object

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

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.

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

env.register_model(ExampleModel)
env.init_tables()

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

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.Id field used as PRIMARY KEY

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 the following SQL:

SELECT "id"
FROM   "test_model"
WHERE  ( "test2" = 'test2'
         AND "test" = 'hello world!' )
        OR "test2" = '2 Hii!!';

Search operators:

  • = Equals to

  • != 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()

env.register_model(ExampleModel)
env.init_tables()

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

env.register_model(ExampleModel)
env.init_tables()

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

sillyorm.sql module

class sillyorm.sql.ColumnInfo(name: str, type: SqlType, constraints: list[SqlConstraint])

Bases: NamedTuple

NamedTuple for describing SQL table columns

constraints: list[SqlConstraint]

Alias for field number 2

name: str

Alias for field number 0

type: SqlType

Alias for field number 1

class sillyorm.sql.Connection

Bases: object

For managing database connections

close() None

Closes the connection

cursor() Cursor

Gets a database cursor from the connection

Returns:

A database cursor

Return type:

sillyorm.sql.Cursor

class sillyorm.sql.Cursor

Bases: object

Abstraction over standard python database cursors with extra features

case_insensitive_like() str

Returns the operator for case-insensitive like in the current DBMS

Returns:

the case-insensitive LIKE operator

Return type:

str

commit() None

Commits the current transaction

ensure_table(name: str, columns: list[ColumnInfo], no_update: bool) None

Makes sure a table with the specified name and columns exists. If any extra columns exist or their type does not match they will be removed. If any columns don’t exist they will be created.

Parameters:
  • name (str) – The name of the table

  • columns (list[sillyorm.sql.ColumnInfo]) – The columns of the table

  • no_update (bool) – If True, do not update anything, just check and if something needs to be updated, throw an exception

execute(sqlcode: SQL) Self

Executes SQL code

Parameters:

sqlcode (sillyorm.sql.SQL) – The SQL code

Returns:

Returns the Cursor

Return type:

sillyorm.sql.Cursor

fetchall() list[tuple[Any, ...]]

Fetches all remaining rows of the query.

Returns:

All remaining rows of the query. Empty list if nothing is available

Return type:

list[tuple[Any, …]]

fetchone() tuple[Any, ...]

Fetches the next row of the query.

Returns:

The next row of the query. None if nothing is available

Return type:

tuple[Any, …]

get_table_column_info(name: str) list[ColumnInfo]

Returns the column info of a table

Parameters:

name (str) – The name of the table

Returns:

The column info of the specified table

Return type:

list[sillyorm.sql.ColumnInfo]

rollback() None

Rolls back the current transaction

table_exists(name: str) bool

Checks if a table exists

Parameters:

name (str) – The name of the table

Returns:

whether or not the table exists

Return type:

bool

class sillyorm.sql.SQL(code: str, **kwargs: Self | str | int | float)

Bases: object

Class for properly constructing and escaping SQL code

Parameters:
  • code (str) – SQL format string

  • **kwargs – arguments for the format string

Warning

The code parameter may ABSOLUTELY not contain ANY user-provided input as that would likely cause SQL injection

Example:

>>> from sillyorm.sql import SQL
>>> where = SQL(
...     "WHERE {id} IN {ids};",
...     id=SQL.identifier("id"),
...     ids=SQL.set([1, 2, 3]),
... )
>>> print(where.code())
WHERE "id" IN (1, 2, 3);
>>> sql = SQL(
...     "SELECT * FROM {table} {where}",
...     table=SQL.identifier("table"),
...     where=where,
... )
>>> print(sql.code())
SELECT * FROM "table" WHERE "id" IN (1, 2, 3);
__add__(sql: Self) Self
code() str

Generates the raw SQL code as a string

>>> from sillyorm.sql import SQL
>>> sql = SQL(
...     "SELECT * FROM {table};",
...     table=SQL.identifier("something"),
... )
>>> print(sql.code())
SELECT * FROM "something";
Returns:

The resulting code

Return type:

str

classmethod commaseperated(values: list[Any] | tuple[Any, ...]) Self

Creates an SQL comma seperated list

>>> from sillyorm.sql import SQL
>>> print(SQL.commaseperated(
...     [SQL.identifier("someid"), 123, 1.2, 'hello world']
... ).code())
"someid", 123, 1.2, 'hello world'
Parameters:

values (list[Any] | tuple[Any, ...]) – The values in the list

Returns:

Returns an instance of the SQL class with the list in it

Return type:

sillyorm.sql.SQL

classmethod escape(value: str | int | float) Self

Escapes values so they can be safely used in SQL

>>> from sillyorm.sql import SQL
>>> print(SQL.escape("hello ' \" world").code())
'hello '' " world'
>>> print(SQL.escape(123).code())
123
Parameters:

value (str | int | float) – Value to escape

Returns:

Returns an instance of the SQL class with the escaped SQL in it

Return type:

sillyorm.sql.SQL

classmethod identifier(name: str) Self

Creates an SQL identifier (surrounded in double quotes) and ensures it does not contain any invalid characters

>>> from sillyorm.sql import SQL
>>> SQL.identifier("hello\"world")
Traceback (most recent call last):
...
sillyorm.exceptions.SillyORMException: invalid SQL identifier
>>> print(SQL.identifier("some_identifier").code())
"some_identifier"
Parameters:

name (str) – The identifier string

Returns:

Returns an instance of the SQL class with the identifier in it

Return type:

sillyorm.sql.SQL

classmethod set(values: list[Any] | tuple[Any, ...]) Self

Creates an SQL set

>>> from sillyorm.sql import SQL
>>> print(SQL.set(
...     [SQL.identifier("someid"), 123, 1.2, 'hello world']
... ).code())
("someid", 123, 1.2, 'hello world')
Parameters:

values (list[Any] | tuple[Any, ...]) – The values in the list

Returns:

Returns an instance of the SQL class with the set in it

Return type:

sillyorm.sql.SQL

classmethod type(t: SqlType) Self

Creates an SQL type

>>> from sillyorm.sql import SQL, SqlType
>>> print(SQL.type(SqlType.varchar(123)).code())
VARCHAR(123)
Parameters:

t (sillyorm.sql.SqlType) – The type

Returns:

Returns an instance of the SQL class with the type in it

Return type:

sillyorm.sql.SQL

class sillyorm.sql.SqlConstraint(kind: str, **kwargs: Any)

Bases: object

Class for SQL constraints

Variables:
  • kind (str) – SQL constraint kind as string

  • args (dict) – Extra arguments (like foreign_table for foreign_key)

Parameters:
  • kind – SQL constraint kind as string

  • **kwargs – The kwargs dict is saved in args

Warning

You should not call the constructor of this class directly.

static foreign_key(foreign_table: str, foreign_column: str) SqlConstraint

FOREIGN KEY SQL constraint

Parameters:
  • foreign_table (str) – Foreign table

  • foreign_column (str) – Foreign column

static not_null() SqlConstraint

NOT NULL SQL constraint

static primary_key() SqlConstraint

PRIMARY KEY SQL constraint

static unique() SqlConstraint

UNIQUE SQL constraint

class sillyorm.sql.SqlType(value: str)

Bases: object

Class for SQL data types

Variables:

value (str) – SQL type as string

Parameters:

value (str) – SQL type as string

Warning

You should not call the constructor of this class directly.

static boolean() SqlType

BOOLEAN SQL type

static date() SqlType

DATE SQL type

Warning

some DBMS include a timestamp for DATE

static float() SqlType

FLOAT SQL type

static integer() SqlType

INTEGER SQL type

static text() SqlType

TEXT SQL type

static timestamp() SqlType

TIMESTAMP SQL type

static varchar(length: int) SqlType

VARCHAR SQL type

Parameters:

length (int) – The maximum length

class sillyorm.sql.TableManager(table_name: str)

Bases: object

Class for managing an SQL table

Parameters:

table_name (str) – SQL table name

delete_records(cr: Cursor, extra_sql: SQL) None

Deletes records

Parameters:
insert_record(cr: Cursor, vals: dict[str, Any]) None

Creates a record

Parameters:
  • cr (sillyorm.sql.Cursor) – The cursor to use

  • vals (dict[str, Any]) – The values for the columns

read_records(cr: Cursor, columns: list[str], extra_sql: SQL) list[dict[str, Any]]

Reads records

Parameters:
  • cr (sillyorm.sql.Cursor) – The cursor to use

  • columns (list[str]) – The names of the columns to return

  • extra_sql (sillyorm.sql.SQL) – Some extra SQL to use for selecting which records to update. Would typically be some WHERE.

Returns:

A list of dictionaries where the key is the column name and the value is the value read from the specified column

Return type:

list[dict[str, Any]]

search_count_records(cr: Cursor, domain: list[str | tuple[str, str, Any]]) int

Counts the amount of records that would be found by a search_records with the specified domain

Parameters:
  • cr (sillyorm.sql.Cursor) – The cursor to use

  • domain (list[str | tuple[str, str, Any]]) – The search domain

Returns:

The amount of records found

Return type:

int

search_records(cr: Cursor, columns: list[str], domain: list[str | tuple[str, str, Any]], order_by: str | None = None, order_asc: bool = True, offset: int | None = None, limit: int | None = None) list[Any]

Searches for records

Parameters:
  • cr (sillyorm.sql.Cursor) – The cursor to use

  • columns (list[str]) – The names of the columns to return

  • domain (list[str | tuple[str, str, Any]]) – The search domain

  • order_by (str | None) – The column to order by

  • order_asc (bool) – Whether 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:

The records found (emtpy list if none were found)

Return type:

list[Any]

table_init(cr: Cursor, columns: list[ColumnInfo], no_update: bool) None

Initializes the database table

Parameters:
  • cr (sillyorm.sql.Cursor) – The cursor to use

  • columns (list[sillyorm.sql.ColumnInfo]) – The columns the table should have

  • no_update (bool) – If True, do not update anything, just check and if something needs to be updated, throw an exception

update_records(cr: Cursor, column_vals: dict[str, Any], extra_sql: SQL) None

Updates records

Parameters:
  • cr (sillyorm.sql.Cursor) – The cursor to use

  • column_vals (dict[str, Any]) – The values for the columns

  • extra_sql (sillyorm.sql.SQL) – Some extra SQL to use for selecting which records to update. Would typically be some WHERE.

Module contents