14

Automapper is a object-object mapper where we can use to project domain model to view model in asp.net mvc.

http://automapper.codeplex.com/

Is there equivalent implementation in Python for use in Django(Template)/Pylons ? Or is there necessity for this in Python world?

stoto
  • 364
  • 1
  • 4
  • 10
  • You should accept godswearhats' answer – object-object mapping is not available for Django/Pylons, and usually isn't necessary in most Python cases, mostly because Python is dynamically typed and allows you to do most of the things object-object mapping is designed to do in other ways. – Fabian Fagerholm Mar 21 '11 at 14:43
  • @Fabian/@godswearhats - What about caching objects to an external service? Most libraries in python use the built-in pickle library which has issues with objects and nested object graphs so this could be a use case for requiring an automapper. – longda Aug 23 '12 at 19:19

4 Answers4

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

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
  • 2,155
  • 1
  • 14
  • 6
  • 12
    Using such tool is not a matter of performance, but can help to implement a layer-based architecture. Even if such architecture is not some common when using python, it can still be a good idea. – Toilal Nov 04 '17 at 15:31
  • 2
    Exactly, @Toilal. That's why we have Marshmallow. I'm surprised no one mentioned it. – code4life Mar 10 '18 at 00:38
  • The idea of using the automapper as @toilal said, is not about performance is about the layers of the architecture and decoupling. – Ernesto Gutierrez Dec 12 '18 at 20:35
0

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

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

andnik
  • 2,405
  • 2
  • 22
  • 33
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/ahancock1/5e5e0c665c3e696f1e8085f7b38bd123

Adam H
  • 561
  • 1
  • 9
  • 20