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 changes:
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:
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