0

I want to softly enforce users in a user base to set a value that previously was not required, but now is.

This is the setup

Users fetched from the database are validated against the database bound marshmallow schema, this allows the None value.

country = fields.String(validate=OneOf(COUNTRY_CODES), allow_none=True)

New users are validated against a marshmallow schema that disallows None.

country = fields.String(validate=OneOf(COUNTRY_CODES), allow_none=False)

Edited users are validated against another marshmallow schema, and here is the tricky part.

I want it to be fine to not set the field, if it is previously not set, but once it is set, you should not be able to remove it.

How is this specified in marshmallow?

firelynx
  • 30,616
  • 9
  • 91
  • 101

1 Answers1

2

I might be misunderstanding your question, but it seems to me, the user being a new user or an existing one, is a state that's external to the schema (as the schema is stateless) and so, it needs to be passed to the validator. In similar cases to yours, I've done something like this:

from marshmallow import fields, validates_schema, Schema
from marshmallow.validate import OneOf
from marshmallow.exceptions import ValidationError

class Foo(Schema):
    country = fields.String(validate=OneOf(('A', 'B')), missing=None)

    # Some sort of hint that indicates if it's a new 
    # user or not. Could be record creation date, etc.
    new_user = fields.Boolean(missing=False)

    @validates_schema
    def check_country(self, instance):
        if instance['new_user'] and instance['country'] is None:
            raise ValidationError("count can not be none")

Observe:

schema = Foo()

schema.load({})    
>>> UnmarshalResult(data={'new_user': False, 'country': None}, errors={})

schema.load({'new_user':True})    
>>> UnmarshalResult(data={'new_user': True, 'country': None}, errors={'_schema': ['count can not be none']})

schema.load({'new_user':True, 'country': 'A'})
>>> UnmarshalResult(data={'new_user': True, 'country': 'A'}, errors={})

schema.load({'new_user':False})
>>> UnmarshalResult(data={'new_user': False, 'country': None}, errors={})
PoP
  • 2,055
  • 3
  • 16
  • 20