Skip to content

Commit

Permalink
Remove outdated docs, add info about type hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
konradhalas committed May 12, 2019
1 parent 71f05ab commit f7432e3
Showing 1 changed file with 27 additions and 138 deletions.
165 changes: 27 additions & 138 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,7 @@ Dacite supports following features:
- unions
- forward references
- collections
- values casting and transformation
- remapping of fields names
- custom type hooks

## Motivation

Expand Down Expand Up @@ -102,20 +101,13 @@ of `dacite.Config` class

Configuration is a (data) class with following fields:

- `remap`
- `flattened`
- `prefixed`
- `cast`
- `transform`
- `type_hooks`
- `forward references`
- `check_types`

The examples below show all features of `from_dict` function and usage
of all `Config` parameters.

Use a dot-notation path if you want to point a nested data class field in
a configuration, e.g. `"a.b"`. It works for all options.

### Nested structures

You can pass a data with nested dictionaries and it will create a proper
Expand Down Expand Up @@ -236,31 +228,12 @@ result = from_dict(data_class=B, data=data)
assert result == B(a_list=[A(x='test1', y=1), A(x='test2', y=2)])
```

### Forward References

Definition of forward references can be passed as a `{'name': Type}` mapping to
`Config.forward_references`. This dict is passed to `typing.get_type_hints()` as the
`globalns` param when evaluating each field's type.

```python
@dataclass
class X:
y: "Y"

@dataclass
class Y:
s: str

data = from_dict(X, {"y": {"s": "text"}}, Config(forward_references={"Y": Y}))
assert data == X(Y("text"))
```

### Remapping
### Type hooks

If your input data key does not match with a data class field name, you
can use `Config.remap` argument to handle this case. You have to pass
dictionary with a following mapping:
`{'data_class_field': 'input_field'}`
You can use `Config.type_hooks` argument if you want to transform the input
data of a data class field with given type into the new value. You have to
pass a following mapping: `{Type: callable}`, where `callable` is a
`Callable[[Any], Any]`.

```python
@dataclass
Expand All @@ -269,116 +242,36 @@ class A:


data = {
'y': 'test',
'x': 'TEST',
}

result = from_dict(data_class=A, data=data, config=Config(remap={'x': 'y'}))
result = from_dict(data_class=A, data=data, config=Config(type_hooks={str: str.lower}))

assert result == A(x='test')

```
### Flattened

You often receive a flat structure which you want to convert to
something more sophisticated. In this case you can use
`Config.flattened` argument. You have to pass list of flattened fields.

```python
@dataclass
class A:
x: str
y: int


@dataclass
class B:
a: A
z: float


data = {
'x': 'test',
'y': 1,
'z': 2.0,
}

result = from_dict(data_class=B, data=data, config=Config(flattened=['a']))

assert result == B(a=A(x='test', y=1), z=2.0)
```

### Prefixed

Sometimes your data is prefixed rather than nested. To handle this case,
you have to use `Config.prefixed` argument, just pass a following
mapping: `{'data_class_field': 'prefix'}`
If a data class field type is a `Optional[T]` you can pass both -
`Optional[T]` or just `T` - as a key in `type_hooks`. The same with generic
collections, e.g. when a field has type `List[T]` you can use `List[T]` to
transform whole collection or `T` to transform each item.

```python
@dataclass
class A:
x: str
y: int


@dataclass
class B:
a: A
z: float


data = {
'a_x': 'test',
'a_y': 1,
'z': 2.0,
}

result = from_dict(data_class=B, data=data, config=Config(prefixed={'a': 'a_'}))

assert result == B(a=A(x='test', y=1), z=2.0)
```

### Casting
### Forward References

Input values are not casted by default. If you want to use field type
information to transform input value from one type to another, you have
to pass given field name as an element of the `Config.cast` argument
list.
Definition of forward references can be passed as a `{'name': Type}` mapping to
`Config.forward_references`. This dict is passed to `typing.get_type_hints()` as the
`globalns` param when evaluating each field's type.

```python
@dataclass
class A:
x: str


data = {
'x': 1,
}

result = from_dict(data_class=A, data=data, config=Config(cast=['x']))

assert result == A(x='1')
```

### Transformation

You can use `Config.transform` argument if you want to transform the
input data into the new value. You have to pass a following mapping:
`{'data_class_field': callable}`, where `callable` is a
`Callable[[Any], Any]`.
class X:
y: "Y"

```python
@dataclass
class A:
x: str


data = {
'x': 'TEST',
}

result = from_dict(data_class=A, data=data, config=Config(transform={'x': str.lower}))
class Y:
s: str

assert result == A(x='test')
data = from_dict(X, {"y": {"s": "text"}}, Config(forward_references={"Y": Y}))
assert data == X(Y("text"))
```

### Types checking
Expand Down Expand Up @@ -416,14 +309,11 @@ exception. There are a few of them:
with a type of a data class field
- `MissingValueError` - raised when you don't provide a value for a
required field
- `InvalidConfigurationError` - raised when you provide a invalid value
(a field name or a input data key) for a configuration
- `UnionMatchError` - raised when provided data does not match any type
of `Union`
- `ForwardReferenceError` - raised when undefined forward reference encountered in
dataclass


## Development

First of all - if you want to submit your pull request, thank you very much!
Expand Down Expand Up @@ -522,11 +412,10 @@ def products():

```

What if we want to validate our data (e.g. check if `code` has 6 characters) or
use something different than simple built-in types (e.g. we want to use
`Decimal` as a type for `price` field)? Such features are out of scope of
`dacite` but we can easily combine it with one of data validation library.
Let's try with [marshmallow](https://marshmallow.readthedocs.io).
What if we want to validate our data (e.g. check if `code` has 6 characters)?
Such features are out of scope of `dacite` but we can easily combine it with
one of data validation library. Let's try with
[marshmallow](https://marshmallow.readthedocs.io).

First of all we have to define our data validation schemas:

Expand Down

0 comments on commit f7432e3

Please sign in to comment.