Marshmallow: Make fields required by default?

Created on 22 Dec 2014  路  8Comments  路  Source: marshmallow-code/marshmallow

From @keleshev in https://github.com/keleshev/ask-me-about-api-design/issues/1:

Why not make required=True the default? I was assuming that fields are required right until I read in the docs that they are not. It's a similar problem with SQLAlchemy nullable=True default that is not obvious, and I've seen many people making mistakes because of that.

feedback welcome

Most helpful comment

@maximkulkin Wouldn't it be easier to use functools.partial:

from marshmallow import fields
import functools

RequiredStr = functools.partial(fields.Str, required=True)

All 8 comments

Re: SQLAlchemy: I mostly see people coming from Django trip up on this, as Django uses null=False by default. I believe SQLAlchemy's justification is to more closely mirror SQL, where NOT NULL must be explicitly set. In SQLAlchemy, you can "solve" this with a variety of ways to default columns to nullable=False implicitly.

Re: default required=True: I don't think it makes a difference either way. I can imagine situations where I want mostly required fields, ones where I want mostly optional fields, and ones with an even mix of both. You'd need to determine how often fields are required to figure out what would result in less typing, but my guess is it would be about 50-50.

One solution is to make required behave like many. All fields default to required=None. The schema can set a default behavior by passing required=True. If a field's required is None when dumping or loading, it behaves the way the schema tells it, or keeps the current behavior required=False if there's no behavior set. (Not sure I actually like this solution, but throwing it out there anyway.)

@davidism Thank you for your input. You bring up an interesting point about how Django's default may shape user's expectations.

I think the "class Meta/constructor" paradigm for configuring this behavior is a solution worth considering. Will have to think more deeply about this.

I've decided against this change for the time being. I don't believe it would improve usability or necessarily meet user expectations any better than the status quo.

_Even though the issue is closed, please let me put my input here in case this topic will brought up again._

I came to Flask world with over than 4 years of Django experience and I was indeed hit by surprise SQLAchemy nullable=True default. I do understand that SQLAlchemy could choose to be as close to SQL as possible, but I don't think that bad design should live on (there are a great number of articles about performance issues, True-False-Null, and others, but it is not the point for this discussion).

IMHO, there is a thing that is common amond SQL, SQLAlchemy, JavaScript, PHP (at least old versions), and Marshmallow, it is bad defaults. It is just safer to be strict instead of silently guiding people to a failure.

_I really like Marshmallow! I just wish it gets perfect._

It is possible to use Schema.on_bind_field in a base Schema class, and inside this method set required field parameter to True.

The problem is that it's impossible to determine, what parameter was explicitly provided by user - True or False.

My point is that it's worth to enhance the generic mechanism of configuring field of schemas.

You can create your own field types by wrapping existing field types and changing them to be required by default:

def required_field_type(field_type):
    class RequiredField(field_type):
        __name__ = 'Required' + field_type.__name__
        def __init__(self, *args, **kwargs):
            kwargs.setdefault('required', True)
            super(RequiredField, RequiredField).__init__(*args, **kwargs)

    return RequiredField

import marshmallow as m
import marshmallow.fields as mf

String = required_field_type(mf.String)
Integer = required_field_type(mf.Integer)
# ...

# all fields required by default
class UserSchema(m.Schema):
    username = String()  
    email = String()
    age = Integer()
    full_name = String(required=False)

@maximkulkin Wouldn't it be easier to use functools.partial:

from marshmallow import fields
import functools

RequiredStr = functools.partial(fields.Str, required=True)

@tuukkamustonen Yeah, that's even better. Didn't know that you can still override value that you've specified in partial call.

Was this page helpful?
0 / 5 - 0 ratings