[Fixed]-Is there a python implementation to .net automapper?

2đź‘Ť

âś…

This generally isn’t necessary in Python. We have some pretty complex domain models and we’re able to use them in our views easily, without noticing any performance issues, and we serve millions of page views a month.

Also remember that “view” in Django == “controller” in MVC, and “template” in Django is “view” in MVC. Hence MTV rather than MVC. Something that tripped me up initially 🙂

If there’s some specific issue you’re running into, post that as a question too …

👤godswearhats

14đź‘Ť

Yes, There is.

ObjectMapper is a class for automatic object mapping. It helps you to create objects between project layers (data layer, service layer, view) in a simple, transparent way.

https://pypi.python.org/pypi/object-mapper

0đź‘Ť

Here is a nice Python automapper that is possible to extend for any framework models:

https://pypi.org/project/py-automapper/

👤andnik

0đź‘Ť

I ended up rolling my own basic version of automapper modelled on the .net version.

from typing import Protocol, TypeVar, Callable

from dataclasses import is_dataclass, fields
from dataclasses import MISSING

S = TypeVar("S")
T = TypeVar("T")


class IProfile(Protocol):

    mappings: dict[tuple[type[S], type[T]], dict[str, Callable[[S], object]]]

    def create_map(self,
                source_type: type[S],
                target_type: type[T],
                **mappings: Callable[[S], object]) -> None:
        ...


class IMapper(Protocol):

    def map(self, data: object, data_type: type[T]) -> T:
        ...


class Profile:

    mappings: dict[tuple[type[S], type[T]], dict[str, Callable[[S], object]]]

    def __init__(self) -> None:

        self.mappings = {}

    def create_map(self,
                source_type: type[S],
                target_type: type[T],
                **mappings: Callable[[S], object]) -> None:

        self.mappings[(source_type, target_type)] = dict(mappings)


class Mapper:

    _mappings: dict[tuple[type[S], type[T]], dict[str, Callable[[S], object]]]

    def __init__(self, profiles: list[IProfile]) -> None:

        self._mappings = {}

        for profile in profiles:
            for key, value in profile.mappings.items():
                self._mappings[key] = value

    def map(self, data: object, data_type: type[T]) -> T:

        if not is_dataclass(data_type):
            raise TypeError("type must be a dataclass")

        mapping_key = (type(data), data_type,)

        data_fields = fields(data_type)
        data_params = {}

        mappings = self._mappings.get(mapping_key, {})

        for field in data_fields:

            field_name, field_type = field.name, field.type
            field_value = getattr(data, field_name, None)

            if is_dataclass(field_type):
                field_value = self.map(field_value, field_type)
            else:
                if field_name in mappings:
                    field_value = mappings[field_name](field_value)

                if not field_value and field.default is not MISSING:
                    field_value = field.default

            data_params[field_name] = field_value

        return data_type(**data_params)

I won’t claim it’s perfect but it works well enough for what I required.

https://gist.github.com/ahanc**k1/5e5e0c665c3e696f1e8085f7b38bd123

👤Adam H

Leave a comment