Let's say I have an entity which is mapped to a postgresql table in sqlalchemy.
@dataclass
class User:
id: int
email: str
password: str
country: str
city: str
is_admin: bool = False
def __eq__(self, other) -> bool:
return self.id == other.id
user_table = sa.Table(
'user',
mapper_registry.metadata,
sa.Column('id', UUID(as_uuid=True), primary_key=True, server_default=sa.text('gen_random_uuid()'), nullable=False),
sa.Column('email', sa.String(100), nullable=False, unique=True),
sa.Column('password', sa.String(128), nullable=False),
sa.Column('country', sa.String(50)),
sa.Column('city', sa.String(50)),
sa.Column('is_admin', sa.Boolean(), server_default=sa.text('false'), nullable=False),
)
user_mapper = mapper_registry.map_imperatively(user_model.User, user_table)
That's not hard. Now I want to move address information into a separate value object inside User entity, but still map it to one table on postgresql side.
@dataclass(frozen=True)
class UserAddress:
country: str | None = None
city: str | None = None
@dataclass
class User:
id: int
email: str
password: str
address: UserAddress = UserAddress()
is_admin: bool = False
def __eq__(self, other) -> bool:
return self.id == other.id
user_table = sa.Table(
'user',
mapper_registry.metadata,
sa.Column('id', UUID(as_uuid=True), primary_key=True, server_default=sa.text('gen_random_uuid()'), nullable=False),
sa.Column('email', sa.String(100), nullable=False, unique=True),
sa.Column('password', sa.String(128), nullable=False),
sa.Column('country', sa.String(50)),
sa.Column('city', sa.String(50)),
sa.Column('is_admin', sa.Boolean(), server_default=sa.text('false'), nullable=False),
)
user_mapper = ???
What is the best way to do this?
UPD: I have found composites in SQLAlchemy documentation, which kinda works:
user_mapper = mapper_registry.map_imperatively(user_model.User, user_table, properties={
'info': composite(user_model.UserInfo, user_table.c.name, user_table.c.last_name, user_table.c.birthdate, user_table.c.phone)
})
BUT it requires UserInfo to have __composite_values__ method, which I want to avoid. I don't want UserInfo class to know anything about db-specific stuff and implement any methods for it.
Is there any other way?