Large Example

SQLAlchemy example

A example API project that uses the library including it’s SQLAlchemy support, can be found here: todo-app-demo

Recursive dataclasses example

A larger one-page example between two recursive dataclasses can be found on this page.

Given a target data structure, a class WorkContract that contains an attribute of type Person.

>>> from dataclasses import dataclass
>>> from enum import Enum, auto
>>> from typing import Optional
>>>
>>> @dataclass
... class Person:
...     first_name: str
...     second_name: str
...     full_name: str
...     age: int
>>>
>>> class Employment(Enum):
...     FULL_TIME = auto()
...     PART_TIME = auto()
...     EXTERNAL = auto()
>>>
>>> @dataclass
... class WorkContract:
...     worker: Person
...     manager: Optional[Person]
...     salary: int
...     signable: bool
...     employment: Employment
...     location: str

We want to have a mapper from the source data structure - SoftwareDeveloperContract with the attribute ContactInfo. Notice that the attribute second_name of Person is called surname in ContactInfo. Other than that, all the attribute names are the same.

Instead of writing:

>>> @dataclass
... class ContactInfo:
...     first_name: str
...     surname: str
...     age: int
...
...     def to_Person(self) -> Person:
...         return Person(
...             first_name=self.first_name,
...             second_name=self.surname,
...             full_name=f"{self.first_name} {self.surname}",
...             age=self.age,
...         )

>>> class ContractType(str, Enum):
...     FULL_TIME = "FULL_TIME"
...     PART_TIME = "PART_TIME"
...     FREELANCER = "FREELANCER"
...     SUBCOMPANY = "SUBCOMPANY"
...
...     def to_Employment(self) -> Employment:
...         if self == ContractType.FULL_TIME:
...             return Employment.FULL_TIME
...         elif self == ContractType.PART_TIME:
...             return Employment.PART_TIME
...         else:
...             return Employment.EXTERNAL
>>>
>>> @dataclass
... class SoftwareDeveloperContract:
...     worker: ContactInfo
...     manager: Optional[ContactInfo]
...     salary: int
...     contract_type: ContractType
...
...     def to_WorkContract(self, location: str) -> WorkContract:
...         return WorkContract(
...             worker=self.worker.to_Person(),
...             manager=(None if self.manager is None else self.manager.to_Person()),
...             salary=self.salary,
...             signable=True,
...             employment=self.contract_type.to_Employment(),
...             location=location,
...         )
>>>
>>> software_developer_contract = SoftwareDeveloperContract(
...     worker=ContactInfo(first_name="John", surname="Page", age=30),
...     manager=ContactInfo(first_name="Jennifer", surname="Coulter", age=35),
...     salary=5000,
...     contract_type=ContractType.FULL_TIME,
... )
>>> software_developer_contract.to_WorkContract(location="New York") 
WorkContract(worker=Person(first_name='John', second_name='Page', full_name='John Page', age=30),
             manager=Person(first_name='Jennifer', second_name='Coulter', full_name='Jennifer Coulter', age=35),
             salary=5000,
             signable=True,
             employment=<Employment.FULL_TIME: 1>,
             location='New York')

you can write:

>>> from dataclass_mapper import map_to, mapper, enum_mapper, from_extra
>>>
>>> @mapper(Person, {
...   "second_name": "surname",
...   "full_name": lambda self: f"{self.first_name} {self.surname}"
... })
... @dataclass
... class ContactInfo:
...     first_name: str
...     surname: str
...     age: int
>>>
>>> @enum_mapper(Employment, {"FREELANCER": Employment.EXTERNAL, "SUBCOMPANY": Employment.EXTERNAL})
... class ContractType(str, Enum):
...     FULL_TIME = "FULL_TIME"
...     PART_TIME = "PART_TIME"
...     FREELANCER = "FREELANCER"
...     SUBCOMPANY = "SUBCOMPANY"
>>>
>>> @mapper(WorkContract, {"signable": lambda: True, "employment": "contract_type", "location": from_extra("location")})
... @dataclass
... class SoftwareDeveloperContract:
...     worker: ContactInfo
...     manager: Optional[ContactInfo]
...     salary: int
...     contract_type: ContractType
>>>
>>> software_developer_contract = SoftwareDeveloperContract(
...     worker=ContactInfo(first_name="John", surname="Page", age=30),
...     manager=ContactInfo(first_name="Jennifer", surname="Coulter", age=35),
...     salary=5000,
...     contract_type=ContractType.FULL_TIME,
... )
>>> map_to(software_developer_contract, WorkContract, extra={"location": "New York"}) 
WorkContract(worker=Person(first_name='John', second_name='Page', full_name='John Page', age=30),
             manager=Person(first_name='Jennifer', second_name='Coulter', full_name='Jennifer Coulter', age=35),
             salary=5000,
             signable=True,
             employment=<Employment.FULL_TIME: 1>,
             location='New York')