flesh out general Collector/Mapper interaction
This commit is contained in:
@@ -1,9 +1,12 @@
|
||||
'''
|
||||
just remembered tomatos aren't vegetables. whoops
|
||||
'''
|
||||
import random
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
from co3.schemas import SQLSchema
|
||||
from co3 import CO3, collate
|
||||
from co3 import CO3, collate, Mapper
|
||||
from co3 import util
|
||||
|
||||
|
||||
@@ -20,6 +23,12 @@ class Tomato(Vegetable):
|
||||
@property
|
||||
def attributes(self):
|
||||
return vars(self)
|
||||
|
||||
def collation_attributes(self, action_key, action_grounp):
|
||||
return {
|
||||
'name': self.name,
|
||||
'state': action_key,
|
||||
}
|
||||
|
||||
@collate('ripe', action_groups=['aging'])
|
||||
def ripen(self):
|
||||
@@ -39,42 +48,58 @@ class Tomato(Vegetable):
|
||||
'pieces': random.randint(2, 12)
|
||||
}
|
||||
|
||||
type_list = [Vegetable, Tomato]
|
||||
|
||||
'''
|
||||
VEGETABLE
|
||||
|
|
||||
TOMATO -- AGING
|
||||
|
|
||||
-- COOKING
|
||||
|
||||
Note: foreign keys need to represent values that could be known by objects _without_ first interacting
|
||||
with a DB. This is slightly non-standard, given how common it is to depend on another table's integer ID
|
||||
(typically a value assigned by the DB using an autoincrement, for example, and not specified explicitly
|
||||
within the insertion body). As a result, SQLTable components need to be able to operate by another unique
|
||||
key when expected to connect to other tables in the hierarchy. Below we use `name` with a UNIQUE constraint
|
||||
for this purpose. Note that having an integer `id` is still perfectly okay so that a table can manage
|
||||
uniqueness of its own rows by default.
|
||||
'''
|
||||
metadata = sa.MetaData()
|
||||
vegetable_table = sa.Table(
|
||||
'vegetable',
|
||||
metadata,
|
||||
sa.Column('id', sa.Integer, primary_key=True),
|
||||
sa.Column('name', sa.String),
|
||||
sa.Column('name', sa.String, unique=True),
|
||||
|
||||
sa.Column('color', sa.String),
|
||||
)
|
||||
tomato_table = sa.Table(
|
||||
'tomato',
|
||||
metadata,
|
||||
sa.Column('id', sa.Integer, primary_key=True),
|
||||
sa.Column('vegetable_id', sa.Integer, util.db.deferred_cd_fkey('vegetables.id')),
|
||||
sa.Column('name', sa.String, util.db.deferred_cd_fkey('vegetable.name'), unique=True),
|
||||
|
||||
sa.Column('radius', sa.Integer),
|
||||
)
|
||||
tomato_aging_table = sa.Table(
|
||||
'tomato_aging_states',
|
||||
metadata,
|
||||
sa.Column('id', sa.Integer, primary_key=True),
|
||||
sa.Column('vegetable_id', sa.Integer, util.db.deferred_cd_fkey('vegetables.id')),
|
||||
sa.Column('name', sa.String, util.db.deferred_cd_fkey('tomato.name'), unique=True),
|
||||
|
||||
sa.Column('state', sa.String),
|
||||
sa.Column('age', sa.Integer),
|
||||
sa.Column('age', sa.Integer),
|
||||
)
|
||||
tomato_cooking_table = sa.Table(
|
||||
'tomato_cooking_states',
|
||||
metadata,
|
||||
sa.Column('id', sa.Integer, primary_key=True),
|
||||
sa.Column('vegetable_id', sa.Integer, util.db.deferred_cd_fkey('vegetables.id')),
|
||||
sa.Column('state', sa.String),
|
||||
sa.Column('name', sa.String, util.db.deferred_cd_fkey('tomato.name'), unique=True),
|
||||
|
||||
sa.Column('state', sa.String),
|
||||
sa.Column('pieces', sa.Integer),
|
||||
)
|
||||
vegetable_schema = SQLSchema.from_metadata(metadata)
|
||||
vegetable_schema = SQLSchema.from_metadata(metadata)
|
||||
vegetable_mapper = Mapper(vegetable_schema)
|
||||
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from co3 import Mapper\n",
|
||||
"\n",
|
||||
"import vegetables"
|
||||
]
|
||||
},
|
||||
@@ -31,13 +29,38 @@
|
||||
"- Need connective function (type to collation) and attribute map. Do we need to this with a subclass? If a func is passed in on init, I can type it appropriately I guess `Callable[[type[CO3],str,str|None],dict]`"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "ef733715-bb75-4263-b216-45e778a06b21",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Usage\n",
|
||||
"The Mapper's primary job is to associate class hierarchies with database components. This can be done in a few ways:\n",
|
||||
"\n",
|
||||
"1. Manually attaching a type reference to a Component\n",
|
||||
"2. Attaching a type reference to a Component's name as registered in a schema\n",
|
||||
"3. Automatically register the CO3 heirarchy to matching schema component names (through transformation)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "d2672422-3596-4eab-ac44-5da617f74b80",
|
||||
"metadata": {
|
||||
"jp-MarkdownHeadingCollapsed": true
|
||||
},
|
||||
"source": [
|
||||
"## Explicit example steps"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"execution_count": 2,
|
||||
"id": "7d80f7b9-7458-4ad4-8c1a-3ea56e796b4e",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from co3 import Mapper\n",
|
||||
"\n",
|
||||
"vegetable_mapper = Mapper(\n",
|
||||
" vegetables.Vegetable,\n",
|
||||
" vegetables.vegetable_schema\n",
|
||||
@@ -46,21 +69,34 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"execution_count": 3,
|
||||
"id": "d24d31b4-c4a6-4a1e-8bea-c44378aadfdd",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"'\\nvegetable_mapper.attach(\\n vegetables.Vegetable,\\n vegetables.vegetable_table,\\n)\\n'"
|
||||
]
|
||||
},
|
||||
"execution_count": 3,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# not valid; tables need to be wrapped in CO3 Components\n",
|
||||
"'''\n",
|
||||
"vegetable_mapper.attach(\n",
|
||||
" vegetables.Vegetable,\n",
|
||||
" vegetables.vegetable_table,\n",
|
||||
")"
|
||||
")\n",
|
||||
"'''"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"execution_count": 4,
|
||||
"id": "f9408562-bf50-4522-909c-318557f85948",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@@ -78,7 +114,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"execution_count": 5,
|
||||
"id": "05fdd404-87ee-4187-832f-2305272758ae",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@@ -96,21 +132,43 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"execution_count": 6,
|
||||
"id": "e9b6af49-a69d-41cc-beae-1b6f171cd2f5",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# attach entire type hierarchy w/ type->name map\n",
|
||||
"vegetable_mapper.attach_hierarchy(\n",
|
||||
"# this might make more sense during init\n",
|
||||
" lambda x:x.__name__.lower())\n",
|
||||
" # this might make more sense during init\n",
|
||||
" vegetables.Vegetable,\n",
|
||||
" lambda x:x.__name__.lower()\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"id": "0fb45a86-5c9b-41b1-a3ab-5691444f175e",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"<co3.components.SQLTable at 0x7f2012b23f80>"
|
||||
]
|
||||
},
|
||||
"execution_count": 9,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"vegetable_mapper.get_collation_comp(vegetables.Tomato, 'cooking')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"id": "2e4336ab-5b5f-484d-815d-164d4b6f40a0",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@@ -118,16 +176,16 @@
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"{'co3_root': vegetables.Vegetable,\n",
|
||||
" 'schema': <co3.schemas.SQLSchema at 0x74ac03f5c8c0>,\n",
|
||||
" 'collector': <co3.collector.Collector at 0x74ac0357ae70>,\n",
|
||||
" 'composer': <co3.composer.Composer at 0x74ac0357a4b0>,\n",
|
||||
" 'attribute_comps': {vegetables.Tomato: <co3.components.SQLTable at 0x74ac09d4a720>},\n",
|
||||
" 'schema': <co3.schemas.SQLSchema at 0x732074224aa0>,\n",
|
||||
" 'collector': <co3.collector.Collector at 0x7320757da120>,\n",
|
||||
" 'composer': <co3.composer.Composer at 0x7320757da9c0>,\n",
|
||||
" 'attribute_comps': {vegetables.Tomato: <co3.components.SQLTable at 0x732074224cb0>},\n",
|
||||
" 'collation_groups': defaultdict(dict,\n",
|
||||
" {vegetables.Tomato: {'aging': <co3.components.SQLTable at 0x74ac03f5cad0>,\n",
|
||||
" 'cooking': <co3.components.SQLTable at 0x74ac03f5cb00>}})}"
|
||||
" {vegetables.Tomato: {'aging': <co3.components.SQLTable at 0x732074224ce0>,\n",
|
||||
" 'cooking': <co3.components.SQLTable at 0x732074224d10>}})}"
|
||||
]
|
||||
},
|
||||
"execution_count": 9,
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
@@ -137,40 +195,187 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"id": "c16786d4-0b71-42d9-97f7-7893c542104e",
|
||||
"cell_type": "markdown",
|
||||
"id": "47859e25-b803-4459-a581-f10bbcfac716",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"tomato = vegetables.Tomato('t1', 5)"
|
||||
"## Holistic attachment"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"id": "884d6753-c763-4e71-824a-711436e203e1",
|
||||
"execution_count": 2,
|
||||
"id": "70c9baed-b870-4021-8949-9b713d863de6",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def attr_name_map(cls):\n",
|
||||
" return f'{cls.__name__.lower()}'\n",
|
||||
"\n",
|
||||
"def coll_name_map(cls, action_group):\n",
|
||||
" return f'{cls.__name__.lower()}_{action_group}_states'\n",
|
||||
"\n",
|
||||
"vegetables.vegetable_mapper.attach_many(\n",
|
||||
" vegetables.type_list,\n",
|
||||
" attr_name_map,\n",
|
||||
" coll_name_map,\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"id": "c16786d4-0b71-42d9-97f7-7893c542104e",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"<vegetables.Tomato at 0x74ac082bacc0>"
|
||||
"{'age': 4}"
|
||||
]
|
||||
},
|
||||
"execution_count": 11,
|
||||
"execution_count": 3,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"tomato"
|
||||
"# create new CO3 descendant\n",
|
||||
"tomato = vegetables.Tomato('t1', 5)\n",
|
||||
"\n",
|
||||
"# test a register collation action\n",
|
||||
"tomato.collate('ripe')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"id": "d7fa94ca-3ecd-4ee3-b0dc-f3b2b65ee47c",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"<Component (SQLTable)> tomato"
|
||||
]
|
||||
},
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"vegetables.vegetable_mapper.get_attribute_comp(vegetables.Tomato)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"id": "1adc3bc5-957f-4b5a-bc2c-2d172675826d",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"{'schema': <co3.schemas.SQLSchema at 0x7ab568224e60>,\n",
|
||||
" 'collector': <co3.collector.Collector at 0x7ab568225190>,\n",
|
||||
" 'composer': <co3.composer.Composer at 0x7ab5682251c0>,\n",
|
||||
" 'attribute_comps': {vegetables.Vegetable: <Component (SQLTable)> vegetable,\n",
|
||||
" vegetables.Tomato: <Component (SQLTable)> tomato},\n",
|
||||
" 'collation_groups': defaultdict(dict,\n",
|
||||
" {vegetables.Vegetable: {},\n",
|
||||
" vegetables.Tomato: {'aging': <Component (SQLTable)> tomato_aging_states,\n",
|
||||
" 'cooking': <Component (SQLTable)> tomato_cooking_states}})}"
|
||||
]
|
||||
},
|
||||
"execution_count": 5,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"vars(vegetables.vegetable_mapper)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"id": "f32d1f65-9b1d-4600-b396-8551fbd1fcf7",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"['3bf42abc-8a12-452f-baf6-38a05fc5d420',\n",
|
||||
" '271b7b84-846e-4d1d-87f6-bcabc90a7b55',\n",
|
||||
" 'f9fc5d16-c5cb-47a7-9eca-7df8a3ba5d10']"
|
||||
]
|
||||
},
|
||||
"execution_count": 6,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"vegetables.vegetable_mapper.collect(tomato, ['ripe'])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"id": "380dfbea-90cc-49fc-aef1-ebb342872632",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"defaultdict(<function co3.collector.Collector.__init__.<locals>.<lambda>()>,\n",
|
||||
" {'3bf42abc-8a12-452f-baf6-38a05fc5d420': (<Component (SQLTable)> vegetable,\n",
|
||||
" {'name': 't1', 'color': 'red'}),\n",
|
||||
" '271b7b84-846e-4d1d-87f6-bcabc90a7b55': (<Component (SQLTable)> tomato,\n",
|
||||
" {'name': 't1', 'radius': 5}),\n",
|
||||
" 'f9fc5d16-c5cb-47a7-9eca-7df8a3ba5d10': (<Component (SQLTable)> tomato_aging_states,\n",
|
||||
" {'name': 't1', 'state': 'ripe', 'age': 1})})"
|
||||
]
|
||||
},
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"vegetables.vegetable_mapper.collector._inserts"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"id": "905bb2a9-9c22-4187-be15-3dd32d206e26",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"{<Component (SQLTable)> vegetable: [{'name': 't1', 'color': 'red'}],\n",
|
||||
" <Component (SQLTable)> tomato: [{'name': 't1', 'radius': 5}],\n",
|
||||
" <Component (SQLTable)> tomato_aging_states: [{'name': 't1',\n",
|
||||
" 'state': 'ripe',\n",
|
||||
" 'age': 1}]}"
|
||||
]
|
||||
},
|
||||
"execution_count": 8,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"vegetables.vegetable_mapper.collector.inserts"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "137d0bf1-940d-448c-91e9-01e7fc4f31b4",
|
||||
"id": "d166b9af-e3ba-4750-9dcb-d8d4e08fe4d3",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
'''
|
||||
just remembered tomatos aren't vegetables. whoops
|
||||
'''
|
||||
import random
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
from co3.schemas import SQLSchema
|
||||
from co3 import CO3, collate
|
||||
from co3 import CO3, collate, Mapper
|
||||
from co3 import util
|
||||
|
||||
|
||||
@@ -20,6 +23,12 @@ class Tomato(Vegetable):
|
||||
@property
|
||||
def attributes(self):
|
||||
return vars(self)
|
||||
|
||||
def collation_attributes(self, action_key, action_grounp):
|
||||
return {
|
||||
'name': self.name,
|
||||
'state': action_key,
|
||||
}
|
||||
|
||||
@collate('ripe', action_groups=['aging'])
|
||||
def ripen(self):
|
||||
@@ -39,42 +48,58 @@ class Tomato(Vegetable):
|
||||
'pieces': random.randint(2, 12)
|
||||
}
|
||||
|
||||
type_list = [Vegetable, Tomato]
|
||||
|
||||
'''
|
||||
VEGETABLE
|
||||
|
|
||||
TOMATO -- AGING
|
||||
|
|
||||
-- COOKING
|
||||
|
||||
Note: foreign keys need to represent values that could be known by objects _without_ first interacting
|
||||
with a DB. This is slightly non-standard, given how common it is to depend on another table's integer ID
|
||||
(typically a value assigned by the DB using an autoincrement, for example, and not specified explicitly
|
||||
within the insertion body). As a result, SQLTable components need to be able to operate by another unique
|
||||
key when expected to connect to other tables in the hierarchy. Below we use `name` with a UNIQUE constraint
|
||||
for this purpose. Note that having an integer `id` is still perfectly okay so that a table can manage
|
||||
uniqueness of its own rows by default.
|
||||
'''
|
||||
metadata = sa.MetaData()
|
||||
vegetable_table = sa.Table(
|
||||
'vegetable',
|
||||
metadata,
|
||||
sa.Column('id', sa.Integer, primary_key=True),
|
||||
sa.Column('name', sa.String),
|
||||
sa.Column('name', sa.String, unique=True),
|
||||
|
||||
sa.Column('color', sa.String),
|
||||
)
|
||||
tomato_table = sa.Table(
|
||||
'tomato',
|
||||
metadata,
|
||||
sa.Column('id', sa.Integer, primary_key=True),
|
||||
sa.Column('vegetable_id', sa.Integer, util.db.deferred_cd_fkey('vegetables.id')),
|
||||
sa.Column('name', sa.String, util.db.deferred_cd_fkey('vegetable.name'), unique=True),
|
||||
|
||||
sa.Column('radius', sa.Integer),
|
||||
)
|
||||
tomato_aging_table = sa.Table(
|
||||
'tomato_aging_states',
|
||||
metadata,
|
||||
sa.Column('id', sa.Integer, primary_key=True),
|
||||
sa.Column('vegetable_id', sa.Integer, util.db.deferred_cd_fkey('vegetables.id')),
|
||||
sa.Column('name', sa.String, util.db.deferred_cd_fkey('tomato.name'), unique=True),
|
||||
|
||||
sa.Column('state', sa.String),
|
||||
sa.Column('age', sa.Integer),
|
||||
sa.Column('age', sa.Integer),
|
||||
)
|
||||
tomato_cooking_table = sa.Table(
|
||||
'tomato_cooking_states',
|
||||
metadata,
|
||||
sa.Column('id', sa.Integer, primary_key=True),
|
||||
sa.Column('vegetable_id', sa.Integer, util.db.deferred_cd_fkey('vegetables.id')),
|
||||
sa.Column('state', sa.String),
|
||||
sa.Column('name', sa.String, util.db.deferred_cd_fkey('tomato.name'), unique=True),
|
||||
|
||||
sa.Column('state', sa.String),
|
||||
sa.Column('pieces', sa.Integer),
|
||||
)
|
||||
vegetable_schema = SQLSchema.from_metadata(metadata)
|
||||
vegetable_schema = SQLSchema.from_metadata(metadata)
|
||||
vegetable_mapper = Mapper(vegetable_schema)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user