implement general composition in ComposableMapper subtype
This commit is contained in:
90
_deprecated/composer.py
Normal file
90
_deprecated/composer.py
Normal file
@@ -0,0 +1,90 @@
|
||||
'''
|
||||
Composer
|
||||
|
||||
Base for manually defining table compositions outside those natural to the schema
|
||||
hierarchy (i.e., constructable by a `CO4.compose()` call).
|
||||
|
||||
Example: suppose we have a simple object hierarchy A(CO4) -> B -> C. C's in-built
|
||||
`compose()` method may not always be desirable when constructing composite tables and
|
||||
running related queries. In this case, a custom Composer can be used to make needed
|
||||
composite tables easier to reference; in the case below, we define the "BC" composite
|
||||
table.
|
||||
|
||||
```
|
||||
class ExampleComposer(Composer):
|
||||
|
||||
@register_table
|
||||
def BC(self):
|
||||
full_B = B.compose(full=True)
|
||||
full_C = C.compose(full=True)
|
||||
|
||||
return full_B.join(
|
||||
full_C,
|
||||
full_B.c.name == full_C.c.name, # TODO: is this fine? or do we need base table refs
|
||||
outer=True
|
||||
)
|
||||
'''
|
||||
from pathlib import Path
|
||||
|
||||
from co3.component import Component
|
||||
|
||||
|
||||
def register_table(table_name=None):
|
||||
'''
|
||||
Registry decorator for defined composer classes. Decorating a class method simply
|
||||
attaches a `table_name` attribute to it, setting it to either a provided value or the
|
||||
name of the method itself. Methods with a `table_name` attribute are later swept up at
|
||||
the class level and placed in the `table_map`.
|
||||
'''
|
||||
def decorator(func):
|
||||
if table_name is None:
|
||||
table_name = func.__name__
|
||||
func.table_name = table_name
|
||||
return func
|
||||
return decorator
|
||||
|
||||
class Composer[C: Component]:
|
||||
'''
|
||||
Base composer wrapper for table groupings.
|
||||
|
||||
The schema is centered around a connected group of tables (via foreign keys). Thus,
|
||||
most operations need to be coordinated across tables. The `accessors` submodules
|
||||
are mostly intended to provide a "secondary layer" over the base set of tables in the
|
||||
schema, exposing common higher level table compositions (i.e., chained JOINs). See
|
||||
concrete instances (e.g., CoreAccess, FTSAccessor) for actual implementations these
|
||||
tables; the base class does not expose
|
||||
|
||||
Tables in subclasses are registered with the `register_table` decorator, automatically
|
||||
indexing them under the provided name and making them available via the `table_map`.
|
||||
'''
|
||||
def __init__(self):
|
||||
self._set_tables()
|
||||
|
||||
def _set_tables(self):
|
||||
'''
|
||||
Skip properties (so appropriate delays can be used), and
|
||||
|
||||
Set the table registry at the class level. This only takes place during the first
|
||||
instantiation of the class, and makes it possible to definitively tie methods to
|
||||
composed tables during lookup with `get_table()`.
|
||||
'''
|
||||
cls = self.__class__
|
||||
|
||||
# in case the class has already be instantiated
|
||||
if hasattr(cls, 'table_map'): return
|
||||
|
||||
table_map = {}
|
||||
for key, value in cls.__dict__.items():
|
||||
if isinstance(value, property):
|
||||
continue # Skip properties
|
||||
if callable(value) and hasattr(value, 'table_name'):
|
||||
table_map[value.table_name] = value(self)
|
||||
|
||||
cls.table_map = table_map
|
||||
|
||||
def get_table(self, table_name):
|
||||
'''
|
||||
Retrieve the named table composition, if defined.
|
||||
'''
|
||||
return self.table_map.get(table_name)
|
||||
|
||||
Reference in New Issue
Block a user