Skip to content

Commit 5e6b44e

Browse files
authored
Merge pull request #138 from python-odin/development
Release 2.2
2 parents 7186e59 + 0f51c3e commit 5e6b44e

17 files changed

Lines changed: 399 additions & 312 deletions

File tree

HISTORY

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
2.2
2+
===
3+
4+
- Fix a bug where annotated resource fields failed to get resolved.
5+
- Fix issues where functools.cached_property does not work on annotated resources as __set_name__
6+
was not being called.
7+
- Updates to documentation
8+
- Introduce integration example for mapping Django models
9+
- Flesh out mapping documentation
10+
- Fix missing link to dict_codec reference.
11+
112
2.1
213
===
314

docs/contents.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ Contents
1010
ref/index
1111
integration/index
1212
examples/index
13+
change-history

docs/integration/django/index.rst

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#######################
2+
Integration with Django
3+
#######################
4+
5+
6+
Mapping Django models to resources
7+
==================================
8+
9+
To utilise a Django database model in a :doc:`/ref/mapping/index` a
10+
``FieldResolver`` is required to identify fields available on the model.
11+
12+
The following is an example of a resolver for Django models.
13+
14+
.. code-block:: python
15+
16+
from odin import registration
17+
from odin.utils import getmeta
18+
from odin.mapping import FieldResolverBase
19+
20+
21+
class ModelFieldResolver(FieldResolverBase):
22+
"""
23+
Field resolver for Django Models
24+
"""
25+
26+
def get_field_dict(self):
27+
meta = getmeta(self.obj)
28+
return {f.attname: f for f in meta.fields}
29+
30+
31+
registration.register_field_resolver(ModelFieldResolver, models.Model)

docs/ref/codecs/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ JSON data Odin provides the same ``load``, ``loads`` style interface.
1111
:maxdepth: 2
1212

1313
csv_codec
14+
dict_codec
1415
json_codec
1516
msgpack_codec
1617
toml_codec

docs/ref/mapping/classes.rst

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,3 +238,20 @@ resources.
238238
There is also the simpler method when only a forward mapping is desired.
239239

240240
.. autofunction:: forward_mapping_factory
241+
242+
243+
Mapping Other Types
244+
===================
245+
246+
Mappers can also be used to map other to and from other object types that are not odin Resources.
247+
248+
.. tip:: There is worked example for :doc:`Django Models </integration/django/index>` in the integration section.
249+
250+
For mapping rules to be determined the mapper must resolve which fields/attributes
251+
are available on a type that can be mapped. This is done using a field resolver
252+
that is associated with the type (or sub type) of the object to be mapped.
253+
254+
The field resolver is a class inherited from ``FieldResolverBase`` that can resolve
255+
what fields are available. This class is them registered with Odins type registry.
256+
257+
.. autoclass:: FieldResolverBase

docs/ref/mapping/helpers.rst

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,30 @@ When defining mappings using the shorthand mappings property these methods simpl
1111
They also provide sensible defaults.
1212

1313
.. automodule:: odin.mapping
14+
:noindex:
1415

1516
.. autofunction:: define
1617

1718
.. autofunction:: assign
19+
20+
21+
Action Helpers
22+
==============
23+
24+
Predefined actions for use in mapping definitions.
25+
26+
.. automodule:: odin.mapping.helpers
27+
28+
.. autofunction:: sum_fields
29+
30+
.. autoclass:: JoinFields
31+
32+
.. autoclass:: SplitField
33+
34+
.. autoclass:: ApplyMapping
35+
36+
37+
Special Mappers
38+
===============
39+
40+
.. autoclass:: NoOpMapper

poetry.lock

Lines changed: 61 additions & 61 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
44

55
[tool.poetry]
66
name = "odin"
7-
version = "2.1"
7+
version = "2.2"
88
description = "Data-structure definition/validation/traversal, mapping and serialisation toolkit for Python"
99
authors = ["Tim Savage <tim@savage.company>"]
1010
license = "BSD-3-Clause"

src/odin/annotated_resource/__init__.py

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,7 @@ def _new_meta_instance(
4242
meta_def: Optional[object],
4343
new_class: "AnnotatedResourceType",
4444
) -> MOT:
45-
"""
46-
Instantiate meta options instance and handle inheritance of required fields
47-
"""
45+
"""Instantiate meta options instance and handle inheritance of required fields."""
4846
base_meta = getattr(new_class, "_meta", None)
4947
new_meta = meta_options_type(meta_def)
5048
new_class.add_to_class("_meta", new_meta)
@@ -73,9 +71,7 @@ def _new_meta_instance(
7371

7472

7573
def _iterate_attrs(attrs: Dict[str, Any]) -> Iterable[Tuple[str, BaseField]]:
76-
"""
77-
Iterate through attributes and combine with annotations
78-
"""
74+
"""Iterate through attributes and combine with annotations."""
7975
annotations = attrs.pop("__annotations__", None) or {}
8076

8177
# Yield any annotations processed into field instances
@@ -92,9 +88,7 @@ def _iterate_attrs(attrs: Dict[str, Any]) -> Iterable[Tuple[str, BaseField]]:
9288
def _add_parent_fields_to_class(
9389
new_class: "AnnotatedResourceType", new_meta: ResourceOptions, parents
9490
):
95-
"""
96-
Iterate through parent attrs and yield fields
97-
"""
91+
"""Iterate through parent attrs and yield fields."""
9892
# All the fields of any type declared on this model
9993
local_field_attr_names = {f.attname for f in new_meta.fields}
10094
field_attr_names = set(local_field_attr_names)
@@ -201,15 +195,15 @@ def add_to_class(cls, name, value):
201195
if hasattr(value, "contribute_to_class"):
202196
value.contribute_to_class(cls, name)
203197
else:
198+
if hasattr(value, "__set_name__"):
199+
value.__set_name__(cls, name)
204200
setattr(cls, name, value)
205201

206202

207203
class AnnotatedResource(
208204
ResourceBase, metaclass=AnnotatedResourceType, meta_options_type=ResourceOptions
209205
):
210-
"""
211-
New Style Resource utilising type annotations for defining fields
212-
"""
206+
"""New Style Resource utilising type annotations for defining fields."""
213207

214208

215209
AResource = AnnotatedResource

src/odin/bases.py

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,16 @@
11
import abc
2-
from typing import Protocol
32

43

54
class ResourceIterable(abc.ABC):
6-
"""
7-
Iterable object that yields resources.
8-
"""
5+
"""Iterable object that yields resources."""
96

107
@abc.abstractmethod
118
def __iter__(self):
12-
"""
13-
Iterate resources
14-
"""
9+
"""Iterate resources"""
1510

1611

1712
class TypedResourceIterable(ResourceIterable, abc.ABC):
18-
"""
19-
Iterable object that yields a specific resource.
20-
"""
13+
"""Iterable object that yields a specific resource."""
2114

2215
def __init__(self, resource_type):
2316
self.resource_type = resource_type

0 commit comments

Comments
 (0)