Object Mapper¶
DTO Records¶
The RickDb object mapper allows the declaration of DTO (data transfer objects) classes, generically known as Records. A record contains a set of attributes and their corresponding field names in the database scope, and some optional additional details such as table name, schema and primary key information. The object mapper purpose is to manage the translation of database fields to object attributes, and vice-versa.
RickDb Records are pure data objects, as they don't depend on or reference any database-specific resource; they only hold attributes and values representing a result row from a given database operation. Attribute names are also independent of their database representation - the mapping between an attribute and the underlying field name is explicit, and performed in the declaration of the class.
It is possible to declare Record objects that map only a subset of fields from a given query result; the additional fields will be ignored.
These properties make Records a suitable format to carry data between architectural boundaries, not only due to their decoupling from the underlying table structure, but also because they can be easily serialized and deserialized.
To achieve better performance, the internal mapping of attributes to database field names is performed at load time within the class declaration context; a decorator patches the class definition with the required internal structures when the file is loaded, instead of handling it in runtime.
Declaring Records¶
Record classes are declared using the @fieldmapper decorator:
from rick_db import fieldmapper
@fieldmapper
class Customer:
id = 'id_customer' # attribute 'id' maps to field 'id_customer'
name = 'name' # attribute 'name' maps to field 'name'
address = 'address' # attribute 'address' maps to field 'address'
city = 'city' # attribute 'city' maps to field 'city'
id_country = 'fk_country' # attribute 'id_country' maps to field 'fk_country'
# access class-level attributes
print(Customer.name) # outputs 'name'
# access object-level attributes
# customer data is loaded via __init__; The key names must match the defined attributes
customer = Customer(id=3, name="John Doe", address="Obere Str.", city="Berlin")
# output: John Doe
print(customer.name)
# output: 'None'
print(customer.id_country)
# output: {'address': 'Obere Str.', 'city': 'Berlin', 'id': 3, 'name': 'John Doe'}
print(customer.asdict())
# output: {'id_customer': 3, 'name': 'John Doe', 'address': 'Obere Str.', 'city': 'Berlin'}
print(customer.asrecord())
It is possible to also provide optional details, such as table or view name, schema and primary key name; these details are quite useful when using RickDb's Repository or Query Builder to provide context for operations. This is probably the most common usage scenario, when designing a multi-tier application:
from rick_db import fieldmapper
@fieldmapper(tablename='customers', pk='id_customer', schema='public')
class Customer:
id = 'id_customer'
name = 'name'
address = 'address'
city = 'city'
id_country = 'fk_country'
Available methods¶
The patching process performed by @fieldmapper copies all the available methods in the base Record class to the defined class. As a result, all Record methods can be used on a @fieldmapper patched class:
| Method | Description |
|---|---|
load(**kwargs) |
Load attribute values from named parameters |
fromrecord(record: dict) |
Load attribute values from a database row dict |
has_pk() |
Returns True if a primary key definition exists |
pk() |
Returns the primary key value |
dbfields() |
Returns a list of database field names |
asdict() |
Converts to a dict using attribute names as keys |
asrecord() |
Converts to a dict using database field names as keys |
fields() |
Returns a list of attribute names that have values set |
items() |
Returns asdict().items() |
values() |
Returns a list of stored values |
fieldmap() |
Returns the internal attribute-to-field mapping dict |
add(**kwargs) |
Add new attribute values from named parameters |
See the Record class documentation for full details and examples.