Repository: marshmallow-code/webargs

Issue:
Parser.parse adds "missing" to original_data
When parsing input, parser.parse adds `'field': missing` to `original_data` for every missing field. This happens only at first level, not in nested schema.

Hopefully, this test case will make this clearer:

```py
from marshmallow import Schema, fields, validates_schema

from webargs.flaskparser import parser

class Location(Schema):

    city = fields.Str()
    country = fields.Str()

class User(Schema):

    name = fields.Str()
    location = fields.Nested(Location)

    @validates_schema(pass_original=True)
    def print_original_data(self, data, original_data):
        print(original_data)

    class Meta:
        strict = True

input_1 = {'name': 'Eric Cartman'}
input_2 = {'location': {'city': 'South Park'}}

# Via pure Marshmallow
User().load(input_1)
User().load(input_2)
# {'name': 'Eric Cartman'}
# {'location': {'city': 'South Park'}}

# Mock Flask request object
class Request():
    def __init__(self, args):
        self.args = args

req_1 = Request(input_1)
req_2 = Request(input_2)

# Via webargs
parser.parse(User, req_1, locations=['query'])
parser.parse(User, req_2, locations=['query'])
# {'location': <marshmallow.missing>, 'name': 'Eric Cartman'}
# {'location': {'city': 'South Park'}, 'name': <marshmallow.missing>}
```

AFAIKT, this should not happen. I'd expect original_data to be the same as when using pure Marshmallow.

I could fix this by modifying `_parse_request`like this:

```py
            argdict = schema.fields
            parsed = {}
            for argname, field_obj in iteritems(argdict):
                parsed_value = self.parse_arg(argname, field_obj, req, locations)
                # If load_from is specified on the field, try to parse from that key
                if parsed_value is missing and field_obj.load_from:
                    parsed_value = self.parse_arg(field_obj.load_from, field_obj, req, locations)
                    argname = field_obj.load_from
                # Only add value if not missing
                if parsed_value is not missing:
                    parsed[argname] = parsed_value
```

Is this the correct thing to do?
Parser.parse adds "missing" to original_data
When parsing input, parser.parse adds `'field': missing` to `original_data` for every missing field. This happens only at first level, not in nested schema.

Hopefully, this test case will make this clearer:

```py
from marshmallow import Schema, fields, validates_schema

from webargs.flaskparser import parser

class Location(Schema):

    city = fields.Str()
    country = fields.Str()

class User(Schema):

    name = fields.Str()
    location = fields.Nested(Location)

    @validates_schema(pass_original=True)
    def print_original_data(self, data, original_data):
        print(original_data)

    class Meta:
        strict = True

input_1 = {'name': 'Eric Cartman'}
input_2 = {'location': {'city': 'South Park'}}

# Via pure Marshmallow
User().load(input_1)
User().load(input_2)
# {'name': 'Eric Cartman'}
# {'location': {'city': 'South Park'}}

# Mock Flask request object
class Request():
    def __init__(self, args):
        self.args = args

req_1 = Request(input_1)
req_2 = Request(input_2)

# Via webargs
parser.parse(User, req_1, locations=['query'])
parser.parse(User, req_2, locations=['query'])
# {'location': <marshmallow.missing>, 'name': 'Eric Cartman'}
# {'location': {'city': 'South Park'}, 'name': <marshmallow.missing>}
```

AFAIKT, this should not happen. I'd expect original_data to be the same as when using pure Marshmallow.

I could fix this by modifying `_parse_request`like this:

```py
            argdict = schema.fields
            parsed = {}
            for argname, field_obj in iteritems(argdict):
                parsed_value = self.parse_arg(argname, field_obj, req, locations)
                # If load_from is specified on the field, try to parse from that key
                if parsed_value is missing and field_obj.load_from:
                    parsed_value = self.parse_arg(field_obj.load_from, field_obj, req, locations)
                    argname = field_obj.load_from
                # Only add value if not missing
                if parsed_value is not missing:
                    parsed[argname] = parsed_value
```

Is this the correct thing to do?


Discussion:

Resolving patch:
diff --git a/webargs/core.py b/webargs/core.py
--- a/webargs/core.py
+++ b/webargs/core.py
@@ -279,7 +279,8 @@ def _parse_request(self, schema, req, locations):
                 if parsed_value is missing and field_obj.load_from:
                     parsed_value = self.parse_arg(field_obj.load_from, field_obj, req, locations)
                     argname = field_obj.load_from
-                parsed[argname] = parsed_value
+                if parsed_value is not missing:
+                    parsed[argname] = parsed_value
         return parsed
 
     def load(self, data, argmap):


Test patch:
diff --git a/tests/test_core.py b/tests/test_core.py
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -5,7 +5,7 @@
 
 import pytest
 import marshmallow
-from marshmallow import Schema, post_load, class_registry
+from marshmallow import Schema, post_load, class_registry, validates_schema
 from werkzeug.datastructures import MultiDict as WerkMultiDict
 
 PY26 = sys.version_info[0] == 2 and int(sys.version_info[1]) < 7
@@ -752,6 +752,21 @@ def viewfunc2(args):
             viewfunc()
         assert excinfo.value.args[0] == {'email': ['Not a valid email address.']}
 
+    # Regression test for https://github.com/sloria/webargs/issues/146
+    def test_parse_does_not_add_missing_values_to_schema_validator(self, web_request, parser):
+        class UserSchema(Schema):
+            name = fields.Str()
+            location = fields.Field(required=False)
+
+            @validates_schema(pass_original=True)
+            def validate_schema(self, data, original_data):
+                assert 'location' not in original_data
+                return True
+
+        web_request.json = {'name': 'Eric Cartman'}
+        res = parser.parse(UserSchema, web_request, locations=('json', ))
+        assert res == {'name': 'Eric Cartman'}
+
 
 def test_use_args_with_custom_locations_in_parser(web_request, parser):
     custom_args = {