[Python] Imperatively map both entity and value object inside that entity to one table

Discussão em 'Python' iniciado por Stack, Julho 10, 2025.

  1. Stack

    Stack Membro Participativo

    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?

    Continue reading...

Compartilhe esta Página