Issue: Support declaring non-serializable fields on a Model
Author: josefdlange
State: closed
Created: 2015-11-03 20:36:34

Hey,

Can you help me think of a way to blacklist certain model fields from being serialized in `model_to_dict`? I can see something like the `Meta` class having a field you can populate with a list of fields, and `model_to_dict` respecting that. The issue I'm running into is that because of the recursive nature of `model_to_dict`, it's not really possible (short of rolling my own entire implementation) to change the behavior of it at each depth.

I've been trying to come up with a solid way of doing this with what peewee currently has to offer, and I'll show you the best I have, but I'm curious if there might be more first-class support for this kind of feature in the future.

More or less, right now I have a base model class (`BaseModel`) that declares two class-level attributes `no_serialize` and `dynamic_serialize` (to add on `extra_attrs` support!) and a property in front of each (`excludes` and `extra_attrs`). The property method recursively follows down foreign keys to aggregate everyone's `no_serialize` and `dynamic_serialize` fields. The class-level attributes may belong in the `Meta` class but I just frankly hadn't gotten around to putting them in the right place.

The `BaseModel` class then implements a method, `to_dict`, which is a convenience wrapper for getting the above properties and calling `model_to_dict` with both those parameters and the ones passed in by the caller.

All in all, it looks like this:

```

class BaseModel(Model):
    created_at = DateTimeField(null=True)
    modified_at = DateTimeField(null=True)
    some_secret_field = CharField(null=True)

    @property
    def lifespan(self):
        return (self.modified_at - self.created_at).total_seconds()

    no_serialize = [some_secret_field]
    dynamic_serialize = [lifespan]

    # TODO the below methods do not handle GFKs, but I'm not too worried about that for now.

    @property
    def excludes(self):
        ex = self.no_serialize
        for f in self._meta.get_fields():
            if isinstance(f, ForeignKeyField):
                ex.extend(f.rel_model.no_serialize)
        return ex

    @property
    def extra_attrs(self):
        dyn = self.dynamic_serialize
        for f in self._meta.get_fields():
            if isinstance(f, ForeignKeyField):
                dyn.extend(f.rel_model.no_serialize)
        return dyn

    def to_dict(self, recurse=False, backrefs=False, only=None, exclude=None, seen=None, extra_attrs=None):
        if exclude and isinstance(exclude, list):
            exclude.extend(self.excludes)
        else:
            exclude = self.excludes

        if only:
            exclude = None

        if extra_attrs:
            extra_attrs.extend(self.extra_attrs)
        else:
            extra_attrs = self.extra_attrs

        return model_to_dict(self, recurse=recurse, backrefs=backrefs, only=only, exclude=exclude, seen=seen, extra_attrs=extra_attrs)
```

This is a really rough attempt at what I'm trying to accomplish, but I'd be glad to take some feedback and contribute what I can to making this a real feature of peewee.

Thanks!


I am going to pass on this particular API or implementation. The `model_to_dict` is a simple but flexible piece of code, but it mostly stands on its own. Introducing APIs at the model level seems like too tight a coupling.