py-automapper icon indicating copy to clipboard operation
py-automapper copied to clipboard

Custom mapping estrange behavior when creating object in SQL Alchemy

Open Carlo1911 opened this issue 1 year ago • 3 comments

I'm trying to add a field mapping, but when it has more than one level it is not getting the value. The value I want to map is inside "TodoDomain.user.id". I want this to work with preregistered mapping (which I cannot make it work)

from dataclasses import dataclass
from typing import Optional
from uuid import uuid4

from automapper import mapper




@dataclass
class UserDomain:
    id: int
    name: str
    email: str


@dataclass
class UserModel:
    id: int
    name: str
    email: str


@dataclass
class TodoDomain:
    description: str
    user: UserDomain


@dataclass
class TodoModel:
    description: str
    user: UserDomain
    user_id: Optional[int] = None


user = UserDomain(id=1, name="carlo", email="mail_carlo")
todo = TodoDomain(description="todo_carlo", user=user)


mapper.add(UserDomain, UserModel)
# This doesn't work. user_id is None
# mapper.add(TodoDomain, TodoModel)

# This works properly. user_id is 1
todo = mapper.to(TodoModel).map(todo, fields_mapping={"user_id": todo.user.id})

print(todo.__dict__)

Carlo1911 avatar Jun 01 '23 15:06 Carlo1911

I am also interested on this. @anikolaienko can you help on this issue?

nerlijman avatar Jun 05 '23 16:06 nerlijman

This is by design. Implicit mapping of fields between different levels can be very confusing and produce unpredictable behaviour. Use explicit mapping as described in your example. E.g.

from dataclasses import dataclass

@dataclass
class UserDomain:
    id: int
    name: str
    email: str


@dataclass
class TodoDomain:
    description: str
    user: UserDomain


@dataclass
class TodoModel:
    description: str
    user: UserDomain
    user_id: Optional[int] = None


user = UserDomain(id=1, name="carlo", email="mail_carlo")
todo = TodoDomain(description="todo_carlo", user=user)

mapper.add(TodoDomain, TodoModel)
todo_model = mapper.map(todo)

# Implicit field mapping between parent and child objects is not supported
# TodoDomain.user.user_id should not map to TodoModel.user_id implicitly
assert todo_model.user_id is None    # True

# Workaround: use explicit mapping
todo_model = mapper.map(todo, fields_mapping={"user_id": todo.user.id})

assert todo_model.user_id == 1     # True

Added a test case: https://github.com/anikolaienko/py-automapper/blob/feature/upgrade-dependencies-and-fix-ci/tests/test_issue_18_implicit_child_field_mapping.py

anikolaienko avatar May 12 '24 15:05 anikolaienko