Generating an ORMatic Interface#

A typical workflow for generating an ORMatic interface consists of these two steps

Identify candidate classes#

  • Choose the dataclasses that represent your persistent domain.

  • Optionally include explicit mapping classes

  • Optionally provide custom type mappings

Create a script generating the interface#

After identifying the persistent part of your domain, I recommend creating a script that generates the interface. The script for generating the test ORM interface for KRROOD looks like this:

import os
from dataclasses import is_dataclass

import krrood.entity_query_language.orm.model
import krrood.entity_query_language.symbol_graph
from dataset.example_classes import (
    PhysicalObject,
    NotMappedParent,
    ChildNotMapped,
    ConceptType,
)
from dataset.semantic_world_like_classes import *
from krrood.class_diagrams.class_diagram import ClassDiagram
from krrood.entity_query_language.predicate import (
    HasTypes,
    HasType,
)
from krrood.entity_query_language.symbol_graph import SymbolGraph
from krrood.ormatic.dao import AlternativeMapping
from krrood.ormatic.ormatic import ORMatic
from krrood.ormatic.utils import classes_of_module
from krrood.utils import recursive_subclasses

# build the symbol graph
symbol_graph = SymbolGraph()

# collect all classes that need persistence
all_classes = {c.clazz for c in symbol_graph._class_diagram.wrapped_classes}
all_classes |= {
    alternative_mapping.original_class()
    for alternative_mapping in recursive_subclasses(AlternativeMapping)
}
all_classes |= set(classes_of_module(krrood.entity_query_language.symbol_graph))
all_classes |= {Symbol}

# remove classes that don't need persistence
all_classes -= {HasType, HasTypes, ContainsType}
all_classes -= {NotMappedParent, ChildNotMapped}

# only keep dataclasses
all_classes = {c for c in all_classes if is_dataclass(c)}

class_diagram = ClassDiagram(
    list(sorted(all_classes, key=lambda c: c.__name__, reverse=True))
)

instance = ORMatic(
    class_dependency_graph=class_diagram,
    type_mappings={
        PhysicalObject: ConceptType,
    },
    alternative_mappings=recursive_subclasses(AlternativeMapping),
)

instance.make_all_tables()

file_path = os.path.join(os.path.dirname(__file__), "dataset", "ormatic_interface.py")

with open(file_path, "w") as f:
    instance.to_sqlalchemy_file(f)

Extending an existing ORMatic interface (Optional)#

If your application depends on a package that already has an ORMatic interface, you can extend it by doing the following:

import dataset.ormatic_interface
from dataset.dataset_extension import AggregatorOfExternalInstances, CustomPosition
from krrood.class_diagrams import ClassDiagram
from krrood.ormatic.ormatic import ORMatic
from krrood.ormatic.utils import get_classes_of_ormatic_interface

# import classes from the existing interface
classes, alternative_mappings, type_mappings = get_classes_of_ormatic_interface(
    dataset.ormatic_interface
)

# specify new classes
classes += [CustomPosition, AggregatorOfExternalInstances]

# create the new ormatic interface
class_diagram = ClassDiagram(
    list(sorted(classes, key=lambda c: c.__name__, reverse=True))
)
instance = ORMatic(
    class_diagram,
    type_mappings=type_mappings,
    alternative_mappings=alternative_mappings,
)
instance.make_all_tables()


new_interface_file = "ormatic_interface.py"
with open(new_interface_file, "w") as f:
    instance.to_sqlalchemy_file(f)