Marshmallow: '_schema': ['Invalid input type.'] is overwriting personalized errors

Created on 2 Oct 2019  ·  4Comments  ·  Source: marshmallow-code/marshmallow

my test:

def test_receive_data_should_respond_400_when_record_type_incorrect(self):
        request = self.client.post(
            url_for('receive.receive_data'),
            json={
                'record_type': 2,
                'record_timestamp': self.date_test,
                'call_identifier': 30,
                'origin_phone': '1234567891',
                'dest_phone': '12345678911'}
        )
        self.assertEqual(request.status_code, 400)
        self.assertEqual(
            request.json['record_type'][0],
            "Err: record_type is 0 for start call and 1 for end call"
        )

my serializer:

 class PhoneCallReceive(ma.Schema):
    class Meta:
        fields = ('record_type',
                  'record_timestamp',
                  'call_identifier',
                  'origin_phone',
                  'dest_phone')

    record_type = fields.Int(
        required=True,
        validate=OneOf(
            choices=[0, 1],
            error='Err: record_type is 0 for start call and 1 for end call')
    )
    record_timestamp = fields.Str(required=True)
    call_identifier = fields.Int(required=True)
    origin_phone = fields.Str(
        required=False,
        validate=Length(
            min=10,
            max=11,
            error='Err: origin_phone should be min={min} and max={max}'
        )
    )
    dest_phone = fields.Str(
        required=False,
        validate=Length(
            min=10,
            max=11,
            error='Err: dest_phone should be min={min} and max={max}'
        )
    )

By sending the json data as the test sends, the serializer should use the record_type validate field to display a personalized error message, but the result is always the same with {'_schema': ['Invalid input type.']}. i cant understand whats happening. is this a bug? a change from 3.0 that i couldnt figure out?

by the way, this is how i temporarily set my route to be after the 3.0

@app.route('/', methods=['POST'])
def receive_data():
    pcr = PhoneCallReceive()
    result = None
    error = None

    try:
        result = pcr.load(request.json)
    except ValidationError as err:
        error = err.normalized_messages()

    result_call, error_call = save_call(result)

    if error or error_call:
        return jsonify(error or error_call), 400

    return pcr.jsonify(result), 201

Most helpful comment

That @pre_load might be the issue.

Backwards-incompatible: Pre/Post-processors MUST return modified data. Returning None does not imply data were mutated (#347).

https://github.com/marshmallow-code/marshmallow/blob/dev/CHANGELOG.rst#300b12-2018-07-04

You may want to change this to a schema level validator since it isn't mutating the data in any way. Waiting until after deserialization to validate should also keep this code from erroring in strange ways when the data is malformed.

https://marshmallow.readthedocs.io/en/stable/extending.html#schema-level-validation

All 4 comments

This error occurs when the data has the wrong structure. It does not appear to be related to the record_type field. You should only see this error if you set many=True and the data isn't an iterable (list), or if many=False (default) and the data is not a mapping (dict). The changelog seems to indicate that we were not properly checking the type until recently. See #930.

Can you log the data you are passing to load?

this is the data passed through pcr.load in this test case, but all tests are kinda similar:

{'call_identifier': 30, 'dest_phone': '12345678911', 'origin_phone': '1234567891', 'record_timestamp': '2019-10-02T13:04:40', 'record_type': 2}

also, i have a pre_load function in the serialize section, goes like this:

    @pre_load(pass_many=True)
    def check_phone_numbers(self, data, many, **kwargs):
        dest_status = 'dest_phone' in data.keys()
        orig_status = 'origin_phone' in data.keys()
        record_type = data.get('record_type')
        if record_type == 1 and any([dest_status, orig_status]):
            raise ValidationError(
                'Err: Phone numbers are not necessary for end calls',
                'phone_validation'
            )
        if record_type == 0 and not all([dest_status, orig_status]):
            raise ValidationError(
                'Err: Please, pass the phone numbers',
                'phone_validation'
            )

That @pre_load might be the issue.

Backwards-incompatible: Pre/Post-processors MUST return modified data. Returning None does not imply data were mutated (#347).

https://github.com/marshmallow-code/marshmallow/blob/dev/CHANGELOG.rst#300b12-2018-07-04

You may want to change this to a schema level validator since it isn't mutating the data in any way. Waiting until after deserialization to validate should also keep this code from erroring in strange ways when the data is malformed.

https://marshmallow.readthedocs.io/en/stable/extending.html#schema-level-validation

by simply adding the return data statement to the function, the problem was solved.
thank you so much for your help, i really appreciate the effort you guys have to help the community.

this issue can be closed, thanks.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

DenisKuplyakov picture DenisKuplyakov  ·  4Comments

Ovyerus picture Ovyerus  ·  3Comments

lupodellasleppa picture lupodellasleppa  ·  3Comments

zohuchneg picture zohuchneg  ·  3Comments

nickretallack picture nickretallack  ·  4Comments