Issue: update_model_from_dict - ignoring field types
Author: OranShuster
State: closed
Created: 2020-05-07 09:56:05

Example model - 
```
class VmSize(Model):
    class Meta:
      ...

    id = PrimaryKeyField()
    cost= DecimalField()
```

if i would use playhous' `model_to_dict` ->`json.dumps` and then `dict_to_model` the value of the decimal field would containt a float (since thats how the json module reads that value when deserializing)

shouldn't there be some check on the field to type and wrap the float value inside a `Decimal` instance?

Peewee doesn't convert types on assignment. It would convert the type before sending it to the database, e.g., if you used it in a query. But you can assign any value to any field type:

```python

In [1]: from peewee import *                                                                    

In [2]: class Foo(Model): 
   ...:     bar = IntegerField() 
   ...:                                                                                         

In [3]: Foo.bar = 'hello'                                                                       

In [4]: Foo.bar                                                                                 
Out[4]: 'hello'
```

This bites hard when the application has to pass JSONs between DB and external sources. The "custom field + `dict_to_model`" combo becomes functionally useless in these cases.

Any plans to alleviate this for custom fields?

If you need data validation then I'd suggest using a dedicated (de)serialization layer between your database and whatever json you need to return. Or implement your own conversions.

Not validation but just to have the right types coming out of the model in general.

Not having that either makes the custom fields useless or leads to duplication of the conversions. Which also makes it impossible to achieve type-invariant code near the database, where that's really useful.

I guess it's as simple as iterating over the fields and calling `python_value()` where needed, so not a big deal, but it's still a bit of a jarring omission.

PR not necessary I suppose?

FTR this is not about conversion on assignment, but the behavior of `dict_to_model`.

Gotcha can you provide an example?

@coleifer In our code, we use multiple custom fields that convert different enum and compound types to basic database types. We take them from their serialized JSON form - which, BTW, also uses basic types. I.e. there's a 1:1 mapping between the types of values we have in the resulting dict to the basic types that the DB uses.

JSON —(`json.loads(...)`)→ `dict` — (`dict_to_model`)→ `Model`.

After a bit of processing, the code sends out similar JSON to the next service in line.

```
from peewee import Field, Model
from playhouse.shortcuts import dict_to_model


class MyFancyType:
    def __bool__(self):  # Oversimplified for the sake of an example
        return True

    def do_something(self):
        return "something"


class FancyField(Field):
    field_type = "BOOL"

    def db_value(self, fancy):
        return bool(fancy)

    def python_value(self, value):
        return MyFancyType()


class MyModel(Model):
    ...
    something_fancy = FancyField()


m = dict_to_model(MyModel, {"something_fancy": True})
m.something_fancy.do_something()  # ⊥ - AttributeError: 'bool' object has no attribute 'do_something'
```

Even if `peewee` does not convert between types on assignment (which I agree with), it would be nice to have `dict_to_model` and `model_to_dict` versions that assume operation on `db_value()` kinds of values.

Current versions imply usage of `python_value` kind, which means that the conversion code from the fields has to be duplicated and applied separately for `dict_to_model` input, negating the value of customs.

Almost all basic db types have 1:1 correspondance with Python's basic types. This would make serializing the model with custom fields a breeze.

@coleifer any comments on this? Should I create a new issue / send PRs?

Doesn't this all boil down to:

>Peewee doesn't convert types on assignment.

The helpers in `shortcuts` (the module name should provide the clue) are just shortcuts. If you want some special behavior, I suggest implementing it yourself in your own module.

You can always iterate your fields after assignment, calling each field's `python_value()`. This is trivial:

```python
def my_dict_to_model(M, data):
    m = dict_to_model(M, data)
    for field in M._meta.sorted_fields:
        setattr(m, f.name, f.python_value(getattr(m, f.name)))
    return m
```

> Doesn't this all boil down to: Peewee doesn't convert types on assignment.

I don't see why this has to be the case for shortcut functions. The model doesn't convert, but why should the shortcuts be limited this way? If this makes it difficult to serialize custom fields - why not just introduce an alternative shortcut that helps with this?

I certainly don't like the idea of rummaging in model internals just to get that to work, but this is what I use for this now.

It just seems like a very odd gap in the user story: Peewee has custom fields, but the shortcuts "do not care" about them in any way - so you might as well not use custom fields if you want to use the shortcuts.