Reputation: 364
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?
Upvotes: 14
Views: 7460
Reputation: 109
The simplest way I could think of mapping objects in python is for them to have some kind of static adapt_...
method. It looks like so:
class PostResponse:
id: int
title: str
text: str
created_at: str
@classmethod
def adapt_db(cls, db_post: Post) -> Self:
model = cls(**{
# basic mapping of identical fields
**db_post.__dict__,
# adjustments for concrete mismatches
'created_at': str(db_post.created_at)
})
return model
db_post = find_post_in_db()
post_response = PostResponse.adapt_db(db_post)
Upvotes: 0
Reputation: 551
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
Upvotes: 0
Reputation: 2804
Here is a nice Python automapper that is possible to extend for any framework models:
https://pypi.org/project/py-automapper/
Upvotes: 0
Reputation: 486
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
Upvotes: 15
Reputation: 2255
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 ...
Upvotes: 2