Adding New Frameworks
Adding a new data model framework is pretty straightforward. There are three key elements involved, all found in the erdantic.base
module.
Field
: The adapter interface for fields on the data model classes from whatever frameworkModel
: The adapter interface for those models themselves@register_model_adapter
: The decorator you use to register a concrete model adapter so thaterdantic
knows about it
You will be making concrete subclasses of the Field
and Model
abstract base classes and filling in several abstract methods.
Some tips:
- You can check out the docstrings on the base classes to understand what methods are supposed to do.
- You can also use the source code for the
erdantic.pydantic
orerdantic.dataclasses
modules as examples. - The erdantic library is thoroughly type-annotated, so you can use a static typechecker like mypy to help you ensure correctness.
Field Subclass
First, make a subclass of Field
. You can use the template below, which stubs out all of the abstract methods, to get started. You should replace MyDataField
—both in the class definition and in the __init__
method definition—with the actual class of field objects in the framework you're adapting. Then fill in the rest of the methods that are being passed.
class MyField(Field[MyDataField]):
def __init__(self, field: MyDataField):
if not isinstance(field, MyDataField):
raise ValueError(f"field must be of type MyDataField. Got: {type(field)}")
super().__init__(field=field)
@property
def name(self) -> str:
pass
@property
def type_obj(self) -> type:
pass
def is_many(self) -> bool:
pass
def is_nullable(self) -> bool:
pass
Model Subclass and Decorator
Next, make a subclass of Model
. It is similarly an abstract base class. Check out the template below with the required methods stubbed. Replace MyDataClass
in the class declaration and in __init__
with the actual class of the data class you're adapting.
You'll also need to decorate this class with @register_model_adapter
. Note that it is actually a decorator factory; calling it with a string input returns the actual decorator. The string input should be a concise unique identifier for your framework, such as the name of its package.
@register_model_adapter("mydataclass")
class MyModel(Model[MyDataClass]):
def __init__(self, model: Type[MyDataClass]):
if not self.is_model_type(model):
raise ValueError(
"Argument model must be a subclass of MyDataClass. "
f"Got {repr_type_with_mro(model)}"
)
super().__init__(model=model)
@staticmethod
def is_model_type(obj: Any) -> bool:
pass
@property
def fields(self) -> List[Field]:
pass