Scene 01
Where the attributes are
The model built by AsqueelDb is a tree of objects. db dot table returns the table object. Its column method returns the model object of a column. Every column object has a dictionary called attributes. It holds the arguments of the declaration, after composition and validation. The table object also has model, the model object of the table. model dot columns maps the physical column names to the same column objects, in declaration order. Reading these objects opens no connection.
Example & screen notes
Slide title "Reading the model". Code, on the database built from ExportInvoicing of lesson 12:
db = AsqueelDb(ExportInvoicing)
contact = db.table('invc.contact')
contact.column('email').attributes['export_mode'] # 'masked'
contact.model.columns['email'] is contact.column('email') # True
list(contact.model.columns)
# ['id', 'customer_id', 'full_name', 'email', 'phone', 'internal_note']
Scene 02
Defaults in the model
The phone column declares neither export argument. Its attributes still contain export_mode equal to plain. When the model is built, a signature default that is not None is written into the attributes. So the application does not repeat the default. export_name has the default None. A None default is not written. Reading it with square brackets would raise KeyError. Use get, with the fallback the application needs.
Example & screen notes
Slide title "What attributes contains". Code:
sorted(contact.column('phone').attributes)
# ['export_mode', 'indexed', 'name', 'notnull', 'unique']
contact.column('phone').attributes['export_mode'] # 'plain'
contact.column('full_name').attributes['export_name'] # 'name'
contact.column('phone').attributes.get('export_name') # None
Two bullets:
- A default other than
Noneis written into the model - An argument with default
None, left out, is absent: read it with.get()
Scene 03
An export driven by the model
This function is application code. asqueel contains nothing like it. It walks the physical columns of a table. It skips the columns marked omit, so internal_note is never queried. It renames the columns that have an export_name. It builds the column list of the query from the model. After the fetch, it masks the columns marked masked. The ids are padded with spaces, because the key columns are character twenty-two. The same function works on any table declared with the export grammar. Changing what is exported means changing the recipe, not the function.
Example & screen notes
Slide title "The application reads, asqueel stores". Code:
def mask(value):
return None if value is None else value[0] + '***'
def export_rows(db, table_name):
table = db.table(table_name)
plan = []
for name, column in table.model.columns.items():
mode = column.attributes['export_mode']
if mode != 'omit':
plan.append((name, column.attributes.get('export_name', name), mode))
columns = ', '.join('$' + name for name, _, _ in plan)
for row in table.query(columns=columns, order_by='$full_name').fetch():
yield {label: mask(row[name]) if mode == 'masked' else row[name]
for name, label, mode in plan}
Output of for row in export_rows(db, 'invc.contact'): print(row):
{'id': 'k1 ', 'customer': 'c1 ', 'name': 'Ann Lee', 'email': 'a***', 'phone': '555-1'}
{'id': 'k2 ', 'customer': 'c2 ', 'name': 'Bo Ng', 'email': 'b***', 'phone': None}
Scene 04
Division of work
asqueel and the application share the work. asqueel accepts only the arguments that the grammar declares. It checks their types and their allowed values. It writes the defaults into the model, after all layers are composed. The application reads the attributes and gives them a meaning. asqueel never executes them. The same model can carry several such vocabularies, one per grammar. An undeclared argument still raises, so a typing mistake in the metadata stops the build.
Example & screen notes
Slide title "Who does what". Table:
| asqueel | The application |
|---|---|
| accepts only declared arguments | decides what an argument means |
checks types and Literal values |
reads attributes at run time |
writes non-None defaults into the model |
acts: export, audit, documentation, ... |
| composes layers before building the model | — |
Scene 05
Standard attributes are read the same way
The standard grammar already has descriptive arguments, such as name_long and caption_field. They work the same way. The database ignores them, and the model keeps them. name_long is also a property. When a column has no name_long, the property returns the short name, or else the column name. That is why the email column returns email. An application reads these attributes exactly like the export attributes of lesson 12.
Example & screen notes
Slide title "Standard semantic attributes". Code:
db.table('invc.invoice').model.name_long # 'Invoice'
db.table('invc.invoice').model.attributes['caption_field'] # 'inv_number'
contact.model.name_long # 'Contact'
contact.column('email').name_long # 'email'
Scene 06
Recap
The model objects expose every declared attribute, standard or added by a grammar. Defaults other than None are already in the model. asqueel stores and validates, and the application reads and acts. The next part of the course, starting with lesson 14, covers queries.
Example & screen notes
Three bullets:
db.table(...).column(name).attributesanddb.table(...).model.columnsexpose the model- Non-
Nonedefaults are in the model;Nonedefaults are absent - asqueel stores and validates; the application reads and acts