{"action":"created","issue":{"url":"https://api.github.com/repos/marshmallow-code/marshmallow/issues/153","labels_url":"https://api.github.com/repos/marshmallow-code/marshmallow/issues/153/labels{/name}","comments_url":"https://api.github.com/repos/marshmallow-code/marshmallow/issues/153/comments","events_url":"https://api.github.com/repos/marshmallow-code/marshmallow/issues/153/events","html_url":"https://github.com/marshmallow-code/marshmallow/issues/153","id":58280936,"number":153,"title":"Processing API for dumping does not match API for loading","user":{"login":"davidism","id":1242887,"avatar_url":"https://avatars.githubusercontent.com/u/1242887?v=3","gravatar_id":"","url":"https://api.github.com/users/davidism","html_url":"https://github.com/davidism","followers_url":"https://api.github.com/users/davidism/followers","following_url":"https://api.github.com/users/davidism/following{/other_user}","gists_url":"https://api.github.com/users/davidism/gists{/gist_id}","starred_url":"https://api.github.com/users/davidism/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/davidism/subscriptions","organizations_url":"https://api.github.com/users/davidism/orgs","repos_url":"https://api.github.com/users/davidism/repos","events_url":"https://api.github.com/users/davidism/events{/privacy}","received_events_url":"https://api.github.com/users/davidism/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/marshmallow-code/marshmallow/labels/feedback%20welcome","name":"feedback welcome","color":"009800"}],"state":"open","locked":false,"assignee":null,"milestone":{"url":"https://api.github.com/repos/marshmallow-code/marshmallow/milestones/3","html_url":"https://github.com/marshmallow-code/marshmallow/milestones/2.0-a","labels_url":"https://api.github.com/repos/marshmallow-code/marshmallow/milestones/3/labels","id":890875,"number":3,"title":"2.0-a","description":"","creator":{"login":"sloria","id":2379650,"avatar_url":"https://avatars.githubusercontent.com/u/2379650?v=3","gravatar_id":"","url":"https://api.github.com/users/sloria","html_url":"https://github.com/sloria","followers_url":"https://api.github.com/users/sloria/followers","following_url":"https://api.github.com/users/sloria/following{/other_user}","gists_url":"https://api.github.com/users/sloria/gists{/gist_id}","starred_url":"https://api.github.com/users/sloria/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sloria/subscriptions","organizations_url":"https://api.github.com/users/sloria/orgs","repos_url":"https://api.github.com/users/sloria/repos","events_url":"https://api.github.com/users/sloria/events{/privacy}","received_events_url":"https://api.github.com/users/sloria/received_events","type":"User","site_admin":false},"open_issues":2,"closed_issues":27,"state":"open","created_at":"2014-12-04T02:25:18Z","updated_at":"2015-03-21T07:18:49Z","due_on":null,"closed_at":null},"comments":9,"created_at":"2015-02-19T22:31:18Z","updated_at":"2015-03-21T20:16:40Z","closed_at":null,"body":"When dumping, I want to manipulate some of the attributes on the object being dumped before actually dumping.  So I decorate a `preprocessor` function.  But that only gets used when *loading*.  I could use a `Method` field, but the data returned from that must be the final serialized form, so there's no way to specify that it's *actually* a `Nested(OtherSchema, many=True)` unless I do that serialization manually at the end of the method.\r\n\r\nWhen loading, I want to manipulate the loaded data in the exact opposite direction as the situation above.  So I decorate a `data_handler` function.  But that only gets used when *dumping*.  The solution is more straightforward here: I override the `make_object` method to manipulate the final output.\r\n\r\nMy point is that the names are different everywhere, some of them are decorators while others are methods, and they don't apply in both directions, when it would be very convenient to be able to do so.  The loading situation is in better shape than the dumping one, since loading has `preprocessor` and `make_object`, except there's still a weird decorator vs. method difference.\r\n\r\nThere should be a standard way to preprocess and postprocess the entire data during both loading and dumping.  One solution is adding `pre_dump`, `post_dump`, `pre_load`, and `post_load` hooks, either as methods on the schema or as decorators."},"comment":{"url":"https://api.github.com/repos/marshmallow-code/marshmallow/issues/comments/84448786","html_url":"https://github.com/marshmallow-code/marshmallow/issues/153#issuecomment-84448786","issue_url":"https://api.github.com/repos/marshmallow-code/marshmallow/issues/153","id":84448786,"user":{"login":"davidism","id":1242887,"avatar_url":"https://avatars.githubusercontent.com/u/1242887?v=3","gravatar_id":"","url":"https://api.github.com/users/davidism","html_url":"https://github.com/davidism","followers_url":"https://api.github.com/users/davidism/followers","following_url":"https://api.github.com/users/davidism/following{/other_user}","gists_url":"https://api.github.com/users/davidism/gists{/gist_id}","starred_url":"https://api.github.com/users/davidism/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/davidism/subscriptions","organizations_url":"https://api.github.com/users/davidism/orgs","repos_url":"https://api.github.com/users/davidism/repos","events_url":"https://api.github.com/users/davidism/events{/privacy}","received_events_url":"https://api.github.com/users/davidism/received_events","type":"User","site_admin":false},"created_at":"2015-03-21T20:16:40Z","updated_at":"2015-03-21T20:16:40Z","body":"Here's what the method definitions might look like.  I'm probably missing details, as I haven't delved into all the combinations the api currently presents.\r\n\r\n* `pre_load(self, data)` gets the raw data and returns data to be deserialized.  It only ever gets one item at a time, removing the need to know if many is True.  (Internally the schema calls it for each item if many is True.)\r\n* `post_load(self, data)` gets the validated, deserialized data and returns the final data/object.  Like `pre_load`, it gets one item at a time.\r\n* `pre_dump` and `post_dump` work the same way\r\n* `validate(self, data, errors)` performs schema-level validation, after all fields are validated individually during (de)serialization.  It puts messages in the errors dict, and returns nothing.  Internally, the schema knows if there are errors now by checking if there are values in the error dict.  This also means that there's no separate \"external\" `validate` vs. `load` methods, just call `load` and check `errors is not None`.\r\n* `error_handler` is removed.  Whatever it does can be done in `validate`.\r\n* `validate_<field name>(self, value)` gets the value *after* it has been (de)serialized by the field, to perform extra validation.  This is a convenience *in addition to* specifying a list of validators on the field.\r\n\r\nThe behavior of `validate` and `validate_<field name>` is more in line with how Django and WTForms do things.\r\n\r\nThe logic for dump or load looks like this: `pre_` → `(de)serialize` → `validate` (schema level) → `post_` (only if no errors).  Within `(de)serialize`, the logic for each field is: `(de)serialize` → `validate_<field name>`.\r\n\r\nThis means there's no way to return data *and* errors at the same time.  Either the data was valid and errors is None, or the data was invalid and is None, and there are errors.  This avoids getting data in some indeterminate state where some of it is valid, some of it has a default/None applied because of errors, etc."}}