Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

flask-marshmallow: how to mark all fields as optional only when the method is PUT

I am making a flask restful api, what I'm having trouble with is marshmallow-sqlalchemy, and webargs

in short here is my sqlalchemy model:

class User(Model):
    id = Column(String, primary_key=True)
    name = Column(String(64), nullable=False)
    email = Column(String(120), nullable=False)
    password = Column(String(128))
    creation_date = Column(DateTime, default=datetime.utcnow)

and this is my schema

class UserSchema(ModelSchema):
    class Meta:
        model = User
        strict = True
        sqla_session = db.session

user_schema = UserSchema()

and an example of my routes using flask-classful and webargs:

class UserView(FlaskView):
    trailing_slash = False
    model = User
    schema = user_schema

    @use_kwargs(schema.fields)
    def post(self, **kwargs):
        try:
            entity = self.model()

            for d in kwargs:
                if kwargs[d] is not missing:
                    entity.__setattr__(d, kwargs[d])

            db.session.add(entity)
            db.session.commit()
            o = self.schema.dump(entity).data
            return jsonify({'{}'.format(self.model.__table__.name): o})

        except IntegrityError:
            return jsonify({'message': '{} exist in the database. choose another id'
                   .format(self.model.__table__.name)}), 409


    @use_kwargs(schema.fields)
    def put(self, id, **kwargs):
        entity = self.model.query.filter_by(id=id).first_or_404()

        for d in kwargs:
            if kwargs[d] is not missing:
                entity.__setattr__(d, kwargs[d])

        db.session.commit()
        o = self.schema.dump(entity).data
        return jsonify({'{}'.format(self.model.__table__.name): o})

UserView.register(app)

The problem: as you can see in my sqlalchemy model, some fields are not nullable, thus my marshmallow schemda marks them as required. My get, index, delete and post methods all work perfectly. But I included post for one reason:

when I try to post a new user with no name for example, a 422 http code is raised because name field is required, which is something I want and it is done perfectly.

BUT when editing fields with put request, I wish EVERYTHING in my schema becomes optional.. right now if I wanted to update a user, I must provide not only the id.. but ALL other information required by default even if I didn't change them at all.

in short, how to mark all fields as "optional" when the method is "put"?

EDIT: just like the solution provided by @Mekicha, I made the following chnages:

change the schema to make the required fields in my model accept the value None. like this:

class UserSchema(ModelSchema):
    class Meta:
        model = User
        ...
    name = fields.Str(missing=None, required=True)
    email = fields.Email(missing=None, required=True)
    ...

change my put and post method condition from this:

if kwargs[d] is not missing:

to this:

if kwargs[d] is not missing and kwargs[d] is not None:
like image 315
RRR Avatar asked May 18 '18 10:05

RRR


People also ask

What is Dump_only?

dump_only – Fields to skip during deserialization (read-only fields) partial – Whether to ignore missing fields and not require any fields declared. Propagates down to Nested fields as well. If its value is an iterable, only missing fields listed in that iterable will be ignored.

What is Flask Smorest?

flask-smorest (formerly known as flask-rest-api) is a database-agnostic framework library for creating REST APIs. It uses Flask as a webserver, and marshmallow to serialize and deserialize data.

What is marshmallow SQLAlchemy?

Marshmallow is a Python library that converts complex data types to and from Python data types. It is a powerful tool for both validating and converting data.


1 Answers

Since you want to make the fields optional during put, how about setting the missing attribute for the fields. From the doc:

missing is used for deserialization if the field is not found in the input data

I think a combination of missing and allow_none(which defaults to True when missing=None) as pointed out here: https://github.com/marshmallow-code/marshmallow/blob/dev/src/marshmallow/fields.py#L89 should work for you

like image 127
Mekicha Avatar answered Sep 16 '22 14:09

Mekicha