Serializing results
We need to return our results. The easiest way to do so is by defining the shape the JSON result should have through a serializer or marshalling model (https://flask-restplus.readthedocs.io/en/stable/marshalling.html).
A serializer model is defined as a dictionary with the expected fields and a field type:
from flask_restplus import fields
model = {
'id': fields.Integer(),
'username': fields.String(),
'text': fields.String(),
'timestamp': fields.DateTime(),
}
thought_model = api_namespace.model('Thought', model)
The model will take a Python object, and convert each of the attributes into the corresponding JSON element, as defined in the field:
@api_namespace.route('/me/thoughts/')
class MeThoughtListCreate(Resource):
@api_namespace.marshal_with(thought_model)
def post(self):
...
new_thought = ThoughtModel(...)
return new_thought
Note that new_thought is a ThoughtModel object, as retrieved by SQLAlchemy. We'll see it in detail next, but for now, it suffices to say that it has all the attributes defined in the model: id, username, text, and timestamp.
Any attribute not present in the memory object will have a value of None by default. You can change this default to a value that will be returned. You can specify a function, so it will be called to retrieve a value when the response is generated. This is a way of adding dynamic information to your object:
model = { 'timestamp': fields.DateTime(default=datetime.utcnow), }
You can also add the name of the attribute to be serialized, in case it's different than the expected outcome, or add a lambda function that will be called to retrieve the value:
model = { 'thought_text': fields.String(attribute='text'),
'thought_username': fields.String(attribute=lambda x: x.username),
}
For more complex objects, you can nest values like this. Note this defines two models from the point of view of the documentation and that each Nested element creates a new scope. You can also use List to add multiple instances of the same kind:
extra = {
'info': fields.String(),
}
extra_info = api_namespace.model('ExtraInfo', extra)
model = { 'extra': fields.Nested(extra),
'extra_list': fields.List(fields.Nested(extra)),
}
Some of the available fields have more options, such as the date format for the DateTime fields. Check the full field's documentation (https://flask-restplus.readthedocs.io/en/stable/api.html#models) for more details.
If you return a list of elements, add the as_list=True parameter in the marshal_with decorator:
@api_namespace.route('/me/thoughts/')
class MeThoughtListCreate(Resource):
@api_namespace.marshal_with(thought_model, as_list=True)
def get(self):
...
thoughts = (
ThoughtModel.query.filter(
ThoughtModel.username == username
)
.order_by('id').all()
)
return thoughts
The marshal_with decorator will transform the result object from a Python object into the corresponding JSON data object.
By default, it will return a http.client.OK (200) status code, but we can return a different status code returning two values: the first is the object to marshal and the second is the status code. The code parameter in the marshal_with decorator is used for documentation purposes. Note, in this case, we need to add the specific marshal call:
@api_namespace.route('/me/thoughts/')
class MeThoughtListCreate(Resource):
@api_namespace.marshal_with(thought_model,
code=http.client.CREATED)
def post(self):
...
result = api_namespace.marshal(new_thought, thought_model)
return result, http.client.CREATED
The Swagger documentation will display all your used-defined marshal objects:
For more details, you can check the full marshalling documentation at https://flask-restplus.readthedocs.io/en/stable/marshalling.html) of Flask-RESTPlus.