Skip to content

ORM API

The Plinx ORM (Object-Relational Mapping) module provides a lightweight interface for working with SQLite databases. This page documents the core classes and methods available in the ORM.

Database Class

plinx.orm.orm.Database

SQLite database wrapper that provides a simple ORM interface.

The Database class is the main entry point for ORM operations in Plinx. It handles database connections, table creation, and provides methods for basic CRUD operations (Create, Read, Update, Delete) on Table objects.

This class uses SQLite as the underlying database engine and provides a simplified interface that avoids writing raw SQL in most cases.

Examples:

Creating a database and defining models:

from plinx.orm import Database, Table, Column, ForeignKey

db = Database("app.db")

class User(Table):
    name = Column(str)
    age = Column(int)

db.create(User)

# Create a new user
john = User(name="John Doe", age=30)
db.save(john)

# Query users
all_users = db.all(User)
john = db.get(User, id=1)

# Update a user
john.age = 31
db.update(john)

# Delete a user
db.delete(john)
Source code in plinx/orm/orm.py
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
class Database:
    """
    SQLite database wrapper that provides a simple ORM interface.

    The Database class is the main entry point for ORM operations in Plinx.
    It handles database connections, table creation, and provides methods for
    basic CRUD operations (Create, Read, Update, Delete) on Table objects.

    This class uses SQLite as the underlying database engine and provides
    a simplified interface that avoids writing raw SQL in most cases.

    Examples:
        Creating a database and defining models:

        ```python
        from plinx.orm import Database, Table, Column, ForeignKey

        db = Database("app.db")

        class User(Table):
            name = Column(str)
            age = Column(int)

        db.create(User)

        # Create a new user
        john = User(name="John Doe", age=30)
        db.save(john)

        # Query users
        all_users = db.all(User)
        john = db.get(User, id=1)

        # Update a user
        john.age = 31
        db.update(john)

        # Delete a user
        db.delete(john)
        ```
    """

    def __init__(self, path: str):
        """
        Initialize a new database connection.

        Args:
            path: Path to the SQLite database file. If the file doesn't exist,
                 it will be created.
        """
        self.connection = sqlite3.Connection(path)

    def create(self, table: "Table"):
        """
        Create a database table based on a Table subclass definition.

        This method creates a table in the database with columns corresponding
        to the Column and ForeignKey attributes defined on the Table subclass.
        If the table already exists, this method does nothing.

        Args:
            table: A Table subclass with Column and/or ForeignKey attributes

        Example:
            ```python
            class User(Table):
                name = Column(str)
                age = Column(int)

            db.create(User)
            ```
        """
        self.connection.execute(table._get_create_sql())

    def save(self, instance: "Table"):
        """
        Save a Table instance to the database.

        This method inserts a new row into the corresponding database table.
        It automatically sets the instance's id attribute to the new row's ID.

        Args:
            instance: A Table instance to save

        Example:
            ```python
            user = User(name="John Doe", age=30)
            db.save(user)  # user.id is now set to the new row's ID
            ```
        """
        sql, values = instance._get_insert_sql()
        cursor = self.connection.execute(sql, values)
        instance._data["id"] = cursor.lastrowid
        self.connection.commit()

    def all(self, table: "Table"):
        """
        Retrieve all rows from a table.

        This method selects all rows from the table corresponding to the given
        Table subclass. It returns a list of instances of that class, with
        attributes set to the values from the database.

        Args:
            table: A Table subclass to query

        Returns:
            List of Table instances, one for each row in the table

        Example:
            ```python
            all_users = db.all(User)
            for user in all_users:
                print(f"{user.name} is {user.age} years old")
            ```
        """
        sql, fields = table._get_select_all_sql()
        rows = self.connection.execute(sql).fetchall()

        result = []

        for row in rows:
            properties = {}
            for field, value in zip(fields, row):
                if field.endswith("_id"):
                    foreign_key = field[:-3]
                    foreign_table = getattr(table, foreign_key).table
                    properties[foreign_key] = self.get(foreign_table, id=value)
                else:
                    properties[field] = value
            result.append(table(**properties))

        return result

    def get(self, table: "Table", **kwargs):
        """
        Retrieve a single row from a table by specified criteria.

        This method selects a row from the database where the specified columns
        match the given values. It returns an instance of the Table subclass with
        attributes set to the values from the database.

        Args:
            table: A Table subclass to query
            **kwargs: Column-value pairs to filter by

        Returns:
            A Table instance corresponding to the matched row

        Raises:
            Exception: If no row matches the criteria

        Example:
            ```python
            # Get user by ID
            user = db.get(User, id=1)

            # Get user by name
            user = db.get(User, name="John Doe")
            ```
        """
        sql, fields, params = table._get_select_where_sql(**kwargs)
        row = self.connection.execute(sql, params).fetchone()

        if row is None:
            raise Exception(f"{table.__name__} instance with {kwargs} does not exist")

        properties = {}

        for field, value in zip(fields, row):
            if field.endswith("_id"):
                foreign_key = field[:-3]
                foreign_table = getattr(table, foreign_key).table
                properties[foreign_key] = self.get(foreign_table, id=value)
            else:
                properties[field] = value

        return table(**properties)

    def update(self, instance: "Table"):
        """
        Update an existing row in the database.

        This method updates the row corresponding to the given instance with the
        current values of the instance's attributes.

        Args:
            instance: A Table instance to update. Must have an id attribute.

        Example:
            ```python
            user = db.get(User, id=1)
            user.name = "Jane Doe"
            db.update(user)
            ```
        """
        sql, values = instance._get_update_sql()
        self.connection.execute(sql, values)
        self.connection.commit()

    def delete(self, instance: "Table"):
        """
        Delete a row from the database.

        This method deletes the row corresponding to the given instance.

        Args:
            instance: A Table instance to delete. Must have an id attribute.

        Example:
            ```python
            user = db.get(User, id=1)
            db.delete(user)
            ```
        """
        sql, values = instance._get_delete_sql()
        self.connection.execute(sql, values)
        self.connection.commit()

    def close(self):
        """
        Close the database connection.

        This method closes the SQLite connection when the database is no longer
        needed. It's good practice to call this method when you're done using
        the database, especially in longer-running applications.
        """
        if self.connection:
            self.connection.close()
        self.connection = None

    @property
    def tables(self):
        """
        Get a list of all tables in the database.

        Returns:
            List of table names as strings
        """
        SELECT_TABLES_SQL = "SELECT name FROM sqlite_master WHERE type = 'table';"
        return [x[0] for x in self.connection.execute(SELECT_TABLES_SQL).fetchall()]

Attributes

tables property

Get a list of all tables in the database.

Returns:

Type Description

List of table names as strings

Functions

__init__(path)

Initialize a new database connection.

Parameters:

Name Type Description Default
path str

Path to the SQLite database file. If the file doesn't exist, it will be created.

required
Source code in plinx/orm/orm.py
52
53
54
55
56
57
58
59
60
def __init__(self, path: str):
    """
    Initialize a new database connection.

    Args:
        path: Path to the SQLite database file. If the file doesn't exist,
             it will be created.
    """
    self.connection = sqlite3.Connection(path)
all(table)

Retrieve all rows from a table.

This method selects all rows from the table corresponding to the given Table subclass. It returns a list of instances of that class, with attributes set to the values from the database.

Parameters:

Name Type Description Default
table Table

A Table subclass to query

required

Returns:

Type Description

List of Table instances, one for each row in the table

Example
all_users = db.all(User)
for user in all_users:
    print(f"{user.name} is {user.age} years old")
Source code in plinx/orm/orm.py
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
def all(self, table: "Table"):
    """
    Retrieve all rows from a table.

    This method selects all rows from the table corresponding to the given
    Table subclass. It returns a list of instances of that class, with
    attributes set to the values from the database.

    Args:
        table: A Table subclass to query

    Returns:
        List of Table instances, one for each row in the table

    Example:
        ```python
        all_users = db.all(User)
        for user in all_users:
            print(f"{user.name} is {user.age} years old")
        ```
    """
    sql, fields = table._get_select_all_sql()
    rows = self.connection.execute(sql).fetchall()

    result = []

    for row in rows:
        properties = {}
        for field, value in zip(fields, row):
            if field.endswith("_id"):
                foreign_key = field[:-3]
                foreign_table = getattr(table, foreign_key).table
                properties[foreign_key] = self.get(foreign_table, id=value)
            else:
                properties[field] = value
        result.append(table(**properties))

    return result
close()

Close the database connection.

This method closes the SQLite connection when the database is no longer needed. It's good practice to call this method when you're done using the database, especially in longer-running applications.

Source code in plinx/orm/orm.py
229
230
231
232
233
234
235
236
237
238
239
def close(self):
    """
    Close the database connection.

    This method closes the SQLite connection when the database is no longer
    needed. It's good practice to call this method when you're done using
    the database, especially in longer-running applications.
    """
    if self.connection:
        self.connection.close()
    self.connection = None
create(table)

Create a database table based on a Table subclass definition.

This method creates a table in the database with columns corresponding to the Column and ForeignKey attributes defined on the Table subclass. If the table already exists, this method does nothing.

Parameters:

Name Type Description Default
table Table

A Table subclass with Column and/or ForeignKey attributes

required
Example
class User(Table):
    name = Column(str)
    age = Column(int)

db.create(User)
Source code in plinx/orm/orm.py
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
def create(self, table: "Table"):
    """
    Create a database table based on a Table subclass definition.

    This method creates a table in the database with columns corresponding
    to the Column and ForeignKey attributes defined on the Table subclass.
    If the table already exists, this method does nothing.

    Args:
        table: A Table subclass with Column and/or ForeignKey attributes

    Example:
        ```python
        class User(Table):
            name = Column(str)
            age = Column(int)

        db.create(User)
        ```
    """
    self.connection.execute(table._get_create_sql())
delete(instance)

Delete a row from the database.

This method deletes the row corresponding to the given instance.

Parameters:

Name Type Description Default
instance Table

A Table instance to delete. Must have an id attribute.

required
Example
user = db.get(User, id=1)
db.delete(user)
Source code in plinx/orm/orm.py
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
def delete(self, instance: "Table"):
    """
    Delete a row from the database.

    This method deletes the row corresponding to the given instance.

    Args:
        instance: A Table instance to delete. Must have an id attribute.

    Example:
        ```python
        user = db.get(User, id=1)
        db.delete(user)
        ```
    """
    sql, values = instance._get_delete_sql()
    self.connection.execute(sql, values)
    self.connection.commit()
get(table, **kwargs)

Retrieve a single row from a table by specified criteria.

This method selects a row from the database where the specified columns match the given values. It returns an instance of the Table subclass with attributes set to the values from the database.

Parameters:

Name Type Description Default
table Table

A Table subclass to query

required
**kwargs

Column-value pairs to filter by

{}

Returns:

Type Description

A Table instance corresponding to the matched row

Raises:

Type Description
Exception

If no row matches the criteria

Example
# Get user by ID
user = db.get(User, id=1)

# Get user by name
user = db.get(User, name="John Doe")
Source code in plinx/orm/orm.py
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
def get(self, table: "Table", **kwargs):
    """
    Retrieve a single row from a table by specified criteria.

    This method selects a row from the database where the specified columns
    match the given values. It returns an instance of the Table subclass with
    attributes set to the values from the database.

    Args:
        table: A Table subclass to query
        **kwargs: Column-value pairs to filter by

    Returns:
        A Table instance corresponding to the matched row

    Raises:
        Exception: If no row matches the criteria

    Example:
        ```python
        # Get user by ID
        user = db.get(User, id=1)

        # Get user by name
        user = db.get(User, name="John Doe")
        ```
    """
    sql, fields, params = table._get_select_where_sql(**kwargs)
    row = self.connection.execute(sql, params).fetchone()

    if row is None:
        raise Exception(f"{table.__name__} instance with {kwargs} does not exist")

    properties = {}

    for field, value in zip(fields, row):
        if field.endswith("_id"):
            foreign_key = field[:-3]
            foreign_table = getattr(table, foreign_key).table
            properties[foreign_key] = self.get(foreign_table, id=value)
        else:
            properties[field] = value

    return table(**properties)
save(instance)

Save a Table instance to the database.

This method inserts a new row into the corresponding database table. It automatically sets the instance's id attribute to the new row's ID.

Parameters:

Name Type Description Default
instance Table

A Table instance to save

required
Example
user = User(name="John Doe", age=30)
db.save(user)  # user.id is now set to the new row's ID
Source code in plinx/orm/orm.py
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
def save(self, instance: "Table"):
    """
    Save a Table instance to the database.

    This method inserts a new row into the corresponding database table.
    It automatically sets the instance's id attribute to the new row's ID.

    Args:
        instance: A Table instance to save

    Example:
        ```python
        user = User(name="John Doe", age=30)
        db.save(user)  # user.id is now set to the new row's ID
        ```
    """
    sql, values = instance._get_insert_sql()
    cursor = self.connection.execute(sql, values)
    instance._data["id"] = cursor.lastrowid
    self.connection.commit()
update(instance)

Update an existing row in the database.

This method updates the row corresponding to the given instance with the current values of the instance's attributes.

Parameters:

Name Type Description Default
instance Table

A Table instance to update. Must have an id attribute.

required
Example
user = db.get(User, id=1)
user.name = "Jane Doe"
db.update(user)
Source code in plinx/orm/orm.py
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
def update(self, instance: "Table"):
    """
    Update an existing row in the database.

    This method updates the row corresponding to the given instance with the
    current values of the instance's attributes.

    Args:
        instance: A Table instance to update. Must have an id attribute.

    Example:
        ```python
        user = db.get(User, id=1)
        user.name = "Jane Doe"
        db.update(user)
        ```
    """
    sql, values = instance._get_update_sql()
    self.connection.execute(sql, values)
    self.connection.commit()

Table Class

plinx.orm.orm.Table

Base class for ORM models in Plinx.

This class is used as a base class for defining database tables. Subclasses should define class attributes using Column and ForeignKey to describe the table schema.

The Table class provides methods for generating SQL statements for CRUD operations, which are used by the Database class.

Examples:

class User(Table):
    name = Column(str)
    age = Column(int)

class Post(Table):
    title = Column(str)
    content = Column(str)
    author = ForeignKey(User)
Source code in plinx/orm/orm.py
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
class Table:
    """
    Base class for ORM models in Plinx.

    This class is used as a base class for defining database tables.
    Subclasses should define class attributes using Column and ForeignKey
    to describe the table schema.

    The Table class provides methods for generating SQL statements for
    CRUD operations, which are used by the Database class.

    Examples:
        ```python
        class User(Table):
            name = Column(str)
            age = Column(int)

        class Post(Table):
            title = Column(str)
            content = Column(str)
            author = ForeignKey(User)
        ```
    """

    def __init__(self, **kwargs):
        """
        Initialize a new record.

        Args:
            **kwargs: Column values to initialize with
        """
        self._data = {"id": None}

        for key, value in kwargs.items():
            self._data[key] = value

    def __getattribute__(self, key):
        """
        Custom attribute access for Table instances.

        This method allows Table instances to access column values as attributes,
        rather than accessing self._data directly.

        Args:
            key: Attribute name to access

        Returns:
            The attribute value
        """
        # Why use super().__getattribute__ instead of self._data[key]?
        # Because otherwise it will create an infinite loop since __getattribute__ will call itself
        # and will never return the value
        _data = super().__getattribute__("_data")
        if key in _data:
            return _data[key]
        return super().__getattribute__(key)

    def __setattr__(self, key, value):
        """
        Custom attribute assignment for Table instances.

        This method ensures that when setting an attribute that corresponds to
        a column, the value is stored in self._data.

        Args:
            key: Attribute name to set
            value: Value to assign
        """
        super().__setattr__(key, value)
        if key in self._data:
            self._data[key] = value

    @classmethod
    def _get_create_sql(cls):
        """
        Generate SQL for creating the table.

        Returns:
            SQL string for creating the table
        """
        CREATE_TABLE_SQL = "CREATE TABLE IF NOT EXISTS {name} ({fields});"
        fields = [
            "id INTEGER PRIMARY KEY AUTOINCREMENT",
        ]

        for name, field in inspect.getmembers(cls):
            if isinstance(field, Column):
                fields.append(f"{name} {field.sql_type}")
            elif isinstance(field, ForeignKey):
                fields.append(f"{name}_id INTEGER")

        fields = ", ".join(fields)
        name = cls.__name__.lower()
        return CREATE_TABLE_SQL.format(name=name, fields=fields)

    def _get_insert_sql(self):
        """
        Generate SQL for inserting a record.

        Returns:
            Tuple of (SQL string, parameter values list)
        """
        INSERT_SQL = "INSERT INTO {name} ({fields}) VALUES ({placeholders});"

        cls = self.__class__
        fields = []
        placeholders = []
        values = []

        for name, field in inspect.getmembers(cls):
            if isinstance(field, Column):
                fields.append(name)
                values.append(getattr(self, name))
                placeholders.append("?")
            elif isinstance(field, ForeignKey):
                fields.append(name + "_id")
                values.append(getattr(self, name).id)
                placeholders.append("?")

        fields = ", ".join(fields)
        placeholders = ", ".join(placeholders)

        sql = INSERT_SQL.format(
            name=cls.__name__.lower(), fields=fields, placeholders=placeholders
        )

        return sql, values

    @classmethod
    def _get_select_all_sql(cls):
        """
        Generate SQL for selecting all records.

        Returns:
            Tuple of (SQL string, field names list)
        """
        SELECT_ALL_SQL = "SELECT {fields} FROM {name};"

        fields = ["id"]

        for name, field in inspect.getmembers(cls):
            if isinstance(field, Column):
                fields.append(name)
            elif isinstance(field, ForeignKey):
                fields.append(name + "_id")

        return (
            SELECT_ALL_SQL.format(
                fields=", ".join(fields),
                name=cls.__name__.lower(),
            ),
            fields,
        )

    @classmethod
    def _get_select_where_sql(cls, **kwargs):
        """
        Generate SQL for selecting records by criteria.

        Args:
            **kwargs: Column-value pairs to filter by

        Returns:
            Tuple of (SQL string, field names list, parameter values list)
        """
        SELECT_WHERE_SQL = "SELECT {fields} FROM {name} WHERE {query};"

        fields = ["id"]
        query = []
        values = []

        for name, field in inspect.getmembers(cls):
            if isinstance(field, Column):
                fields.append(name)
            elif isinstance(field, ForeignKey):
                fields.append(name + "_id")

        for key, value in kwargs.items():
            query.append(f"{key} = ?")
            values.append(value)

        return (
            SELECT_WHERE_SQL.format(
                fields=", ".join(fields),
                name=cls.__name__.lower(),
                query=", ".join(query),
            ),
            fields,
            values,
        )

    def _get_update_sql(self):
        """
        Generate SQL for updating a record.

        Returns:
            Tuple of (SQL string, parameter values list)
        """
        UPDATE_SQL = "UPDATE {name} SET {fields} WHERE id = ?;"

        cls = self.__class__
        fields = []
        values = []

        for name, field in inspect.getmembers(cls):
            if isinstance(field, Column):
                fields.append(name)
                values.append(getattr(self, name))
            elif isinstance(field, ForeignKey):
                fields.append(name + "_id")
                values.append(getattr(self, name).id)

        values.append(getattr(self, "id"))

        return (
            UPDATE_SQL.format(
                name=cls.__name__.lower(),
                fields=", ".join([f"{field} = ?" for field in fields]),
            ),
            values,
        )

    def _get_delete_sql(self):
        """
        Generate SQL for deleting a record.

        Returns:
            Tuple of (SQL string, parameter values list)
        """
        DELETE_SQL = "DELETE FROM {name} WHERE id = ?;"

        return DELETE_SQL.format(name=self.__class__.__name__.lower()), [
            getattr(self, "id")
        ]

Functions

__getattribute__(key)

Custom attribute access for Table instances.

This method allows Table instances to access column values as attributes, rather than accessing self._data directly.

Parameters:

Name Type Description Default
key

Attribute name to access

required

Returns:

Type Description

The attribute value

Source code in plinx/orm/orm.py
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
def __getattribute__(self, key):
    """
    Custom attribute access for Table instances.

    This method allows Table instances to access column values as attributes,
    rather than accessing self._data directly.

    Args:
        key: Attribute name to access

    Returns:
        The attribute value
    """
    # Why use super().__getattribute__ instead of self._data[key]?
    # Because otherwise it will create an infinite loop since __getattribute__ will call itself
    # and will never return the value
    _data = super().__getattribute__("_data")
    if key in _data:
        return _data[key]
    return super().__getattribute__(key)
__init__(**kwargs)

Initialize a new record.

Parameters:

Name Type Description Default
**kwargs

Column values to initialize with

{}
Source code in plinx/orm/orm.py
341
342
343
344
345
346
347
348
349
350
351
def __init__(self, **kwargs):
    """
    Initialize a new record.

    Args:
        **kwargs: Column values to initialize with
    """
    self._data = {"id": None}

    for key, value in kwargs.items():
        self._data[key] = value
__setattr__(key, value)

Custom attribute assignment for Table instances.

This method ensures that when setting an attribute that corresponds to a column, the value is stored in self._data.

Parameters:

Name Type Description Default
key

Attribute name to set

required
value

Value to assign

required
Source code in plinx/orm/orm.py
374
375
376
377
378
379
380
381
382
383
384
385
386
387
def __setattr__(self, key, value):
    """
    Custom attribute assignment for Table instances.

    This method ensures that when setting an attribute that corresponds to
    a column, the value is stored in self._data.

    Args:
        key: Attribute name to set
        value: Value to assign
    """
    super().__setattr__(key, value)
    if key in self._data:
        self._data[key] = value

Column Class

plinx.orm.orm.Column

Define a column in a database table.

This class represents a column definition for a Table class. It stores the column's type and can generate the corresponding SQL type.

Examples:

class User(Table):
    name = Column(str)  # TEXT column
    age = Column(int)   # INTEGER column
    active = Column(bool)  # INTEGER column (0=False, 1=True)
Source code in plinx/orm/orm.py
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
class Column:
    """
    Define a column in a database table.

    This class represents a column definition for a Table class. It stores
    the column's type and can generate the corresponding SQL type.

    Examples:
        ```python
        class User(Table):
            name = Column(str)  # TEXT column
            age = Column(int)   # INTEGER column
            active = Column(bool)  # INTEGER column (0=False, 1=True)
        ```
    """

    def __init__(self, type: Generic[T]):
        """
        Initialize a new column.

        Args:
            type: Python type for the column (str, int, float, bool, bytes)
        """
        self.type = type

    @property
    def sql_type(self):
        """
        Get the SQL type corresponding to this column's Python type.

        Returns:
            SQL type string (e.g., "TEXT", "INTEGER", "REAL")
        """
        return SQLITE_TYPE_MAP[self.type]

Attributes

sql_type property

Get the SQL type corresponding to this column's Python type.

Returns:

Type Description

SQL type string (e.g., "TEXT", "INTEGER", "REAL")

Functions

__init__(type)

Initialize a new column.

Parameters:

Name Type Description Default
type Generic[T]

Python type for the column (str, int, float, bool, bytes)

required
Source code in plinx/orm/orm.py
269
270
271
272
273
274
275
276
def __init__(self, type: Generic[T]):
    """
    Initialize a new column.

    Args:
        type: Python type for the column (str, int, float, bool, bytes)
    """
    self.type = type

ForeignKey Class

plinx.orm.orm.ForeignKey

Define a foreign key relationship between tables.

This class represents a foreign key constraint in a database schema, linking one Table class to another.

Examples:

class Author(Table):
    name = Column(str)

class Book(Table):
    title = Column(str)
    author = ForeignKey(Author)  # Creates author_id column
Source code in plinx/orm/orm.py
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
class ForeignKey:
    """
    Define a foreign key relationship between tables.

    This class represents a foreign key constraint in a database schema,
    linking one Table class to another.

    Examples:
        ```python
        class Author(Table):
            name = Column(str)

        class Book(Table):
            title = Column(str)
            author = ForeignKey(Author)  # Creates author_id column
        ```
    """

    def __init__(self, table):
        """
        Initialize a new foreign key.

        Args:
            table: The Table subclass that this foreign key references
        """
        self.table = table

Functions

__init__(table)

Initialize a new foreign key.

Parameters:

Name Type Description Default
table

The Table subclass that this foreign key references

required
Source code in plinx/orm/orm.py
307
308
309
310
311
312
313
314
def __init__(self, table):
    """
    Initialize a new foreign key.

    Args:
        table: The Table subclass that this foreign key references
    """
    self.table = table

Type Mapping

The ORM maps Python types to SQLite types according to the following table:

Python Type SQLite Type
str TEXT
int INTEGER
float REAL
bool INTEGER
bytes BLOB

Examples

Basic ORM Usage

from plinx.orm import Database, Table, Column, ForeignKey

# Connect to database
db = Database("example.db")

# Define tables
class User(Table):
    name = Column(str)
    age = Column(int)

class Post(Table):
    title = Column(str)
    content = Column(str)
    author = ForeignKey(User)

# Create tables
db.create(User)
db.create(Post)

# Create records
user = User(name="John Doe", age=30)
db.save(user)

post = Post(title="Hello World", content="This is my first post", author=user)
db.save(post)

# Query records
all_users = db.all(User)
for user in all_users:
    print(f"User: {user.name}, Age: {user.age}")

# Get a specific record
post = db.get(Post, id=1)
print(f"Post: {post.title} by {post.author.name}")

# Update a record
post.content = "Updated content"
db.update(post)

# Delete a record
db.delete(post)

# Close connection when done
db.close()

Advanced Querying

# Get by non-primary key field
try:
    user = db.get(User, name="John Doe")
    print(f"Found user: {user.name}")
except Exception as e:
    print(f"User not found: {e}")

Table Introspection

# List all tables in the database
tables = db.tables
print(f"Tables in database: {tables}")

Custom Table Methods

You can add custom methods to your table classes to encapsulate business logic:

class User(Table):
    name = Column(str)
    email = Column(str)
    age = Column(int)

    def is_adult(self):
        return self.age >= 18

    def get_email_domain(self):
        return self.email.split('@')[1] if '@' in self.email else None

# Usage
user = db.get(User, id=1)
if user.is_adult():
    print(f"{user.name} is an adult")

Working with Relationships

class Department(Table):
    name = Column(str)

class Employee(Table):
    name = Column(str)
    department = ForeignKey(Department)
    salary = Column(float)

# Create tables
db.create(Department)
db.create(Employee)

# Create related records
engineering = Department(name="Engineering")
db.save(engineering)

alice = Employee(name="Alice", department=engineering, salary=75000.0)
bob = Employee(name="Bob", department=engineering, salary=85000.0)
db.save(alice)
db.save(bob)

# Query with relationship
employee = db.get(Employee, name="Alice")
print(f"{employee.name} works in {employee.department.name}")

Limitations

The current version of the Plinx ORM has the following limitations:

  1. SQLite Only: Only supports SQLite databases
  2. Basic Querying: No complex filtering, ordering, or joins in queries
  3. No Migrations: No built-in support for schema migrations
  4. Eager Loading: All relationships are eagerly loaded (no lazy loading)
  5. Simple Relationships: Only supports one-to-many relationships via ForeignKey

These limitations are intentional to keep the ORM simple and focused while still being useful for many common scenarios.