Advanced Topics

Automated Schema Definition

Validating ORM agnosticism for a moment, let’s see how we could utilize __lima_args__['include'] to create our Schema automatically.

We start with this SQLAlchemy model (skip this section if you don’t want to install SQLAlchemy):

import sqlalchemy as sa
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class Account(Base):
    __tablename__ = 'accounts'
    id = sa.Column(sa.Integer, primary_key=True)
    login = sa.Column(sa.String)
    password_hash = sa.Column(sa.String)

lima.fields defines a mapping lima.fields.TYPE_MAPPING of some Python types to field classes. We can utilize this as follows:

from lima import fields

def fields_for_model(model):
    result = {}
    for name, col in model.__mapper__.columns.items():
        field_class = fields.TYPE_MAPPING[col.type.python_type]
        result[name] = field_class()
    return result

Defining lima schemas becomes a piece of cake now:

from lima import Schema

class AccountSchema(Schema):
    __lima_args__ = {'include': fields_for_model(Account)}

dict(AccountSchema.__fields__)
# {'id': <lima.fields.Integer at 0x...>,
#  'login': <lima.fields.String at 0x...>,
#  'password_hash': <lima.fields.String at 0x...>}

... and of course you still can manually add, exclude or inherit anything you like.

Warning

Neither lima.fields.TYPE_MAPPING nor the available field classes are as exhaustive as they should be. Expect above code to fail on slightly exotic column types. There is still work to be done.

Field Name Mangling

Fields specified via __lima_args__['include'] can have arbitrary names. Fields provided via class attributes have a drawback: class attribute names have to be valid Python identifiers.

lima implements a simple name mangling mechanism to allow the specification of some common non-Python-identifier field names (like JSON-LD‘s "@id") as class attributes.

The following table shows how name prefixes will be replaced by lima when specifying fields as class attributes (note that every one of those prefixes ends with a double underscore):

name prefix replacement
'at__' '@'
'dash__' '-'
'dot__' '.'
'hash__' '#'
'plus__' '+'
'nil__' '' (the emtpy String)

This enables us to do the following:

class FancyFieldNamesSchema(Schema):
    at__foo = fields.String(attr='foo')
    hash__bar = fields.String(attr='bar')
    nil__class = fields.String(attr='cls')  # Python Keyword

list(FancyFieldNamesSchema.__fields__)
# ['@foo', '#bar', 'class']

Note

When using field names that aren’t Python identifiers, lima obviously can’t look for attributes with those same names, so make sure to specify explicitly how the data for these fields should be determined (see How a Field gets its Data).

Advanced Topics Recap

  • You are now able to create schemas automatically (__lima_args__['include'] with some model-specific code).
  • You can specify a field named '@context' as a schema class attribute (using field name mangling: 'at__context').