Using Marshmallow Fields in Open Event API Server

The nextgen Open Event API Server  provides API endpoints to fetch the data, and to modify and update it. These endpoints have been written using flask-rest-jsonapi, which is a flask extension to build APIs around the specifications provided by JSONAPI 1.0. This extension helps you, quoting from their website:

flask-rest-jsonAPI’s data abstraction layer lets us expose the resources in a flexible way. This is achieved by using Marshmallow fields by marshmallow-jsonapi. This blog post explains how we use the marshmallow fields for building API endpoints in Open Event API Server.

The marshmallow library is used to serialize, deserialize and validate input data. Marshmallow uses classes to define output schemas. This makes it easier to reuse and configure the and also extend the schemas. When we write the API Schema for any database model from the Open Event models, all the columns have to be added as schema fields in the API class.

This is the API Server’s event schema using marshmallow fields:

These are the Marshmallow Field classes for various types of data. You can pass the following parameters when creating a field object. The ones which are used in the API Server as described below. For the rest, you can read more on marshmallow docs.

Let’s take a look at each of these fields. Each of the following snippets sample writing fields for API Schema.

identifier = fields.Str(dump_only=True)
  • This is a field of data-type String.
  • dump_only :  This field will be skipped during deserialization as it is set to True here. Setting this true essentially means marking `identifier` as read-only( for HTTP API) 
name = fields.Str(required=True)
  • This again is a field of data-type String.
  • This is a required field and a ValidationError is raised if found missing during deserialization. Taking a look at the database backend:

Since this field is set to non-nullable in the database model, it is made required in API Schema.

 external_event_url = fields.Url(allow_none=True)
  • This is a field of datatype URL.
  • Since this is not a required field, NULL values are allowed for this field in the database model. To reflect the same in the API, we have to add allow_none=True. If missing=None is unset, it defaults to false.
ends_at = fields.DateTime(required=Truetimezone=True)
  • Field of datatype DateTime
  • It is a required field for an event and the time used here is timezone aware.
latitude = fields.Float(validate=lambda n: -90 <= n <= 90allow_none=True)
  • Field of datatype Float.
  • In marshmallow fields, we can use validator clauses on the input value of a field using the validate: parameter. It returns a boolean, which when false raises a Validation Error. These validators are called during deserialization.
is_map_shown = fields.Bool(default=False)
  • Field of datatype boolean.
  • Default value for the marshmallow fields can be set by defining using default: Here, is_map_shown attribute is set to false as default for an event.
privacy = fields.Str(default="public")
  • privacy is set to default “public”.

When the input value for a field is missing during serialization, the default value will be used. This parameter can either be a value or a callable.

As described in the examples above, you can write the field as field.<data-type>(*parameters to marshmallow.fields.Field constructor*).

The parameters passed to the class constructor must reflect the column definition in the database model, else you might run into unexpected errors. An example to quote from Open Event development would be that null values were not being allowed to be posted even for nullable columns. This behavior was because allow_none defaults to false in schema, and it has to be explicitly set to True in order to receive null values. ( Issue for the same: Make non-required attributes nullable and the Pull Request made for fix.)

Fields represent a database model column and are serialized and deserialized, so that these can be used in any format, like JSON objects which we use in API server. Each field corresponds to an attribute of the object type like location, starts-at, ends-at, event-url for an event. marshmallow allows us to define data-types for the fields, validate input data and reinforce column level constraints from database model.

This list is not exhaustive of all the parameters available for marshmallow fields. To read further about them and marshmallow, check out their documentation.

Additional Resources

Code involved in API Server:

Continue Reading

Working With Inter-related Resource Endpoints In Open Event API Server

In this blogpost I will discuss how the resource inter-related endpoints work. These are the endpoints which involve two resource objects which are also related to another same resource object.


The discussion in this post is of the endpoints related to Sessions Model. In the API server, there exists a relationship between event and sessions. Apart from these, session also has relationships with microlocations, tracks, speakers and session-types. Let’s take a look at the endpoints related with the above.

`/v1/tracks/<int:track_id>/sessions` is a list endpoint which can be used to list and create the sessions related to a particular track of an event. To get the list we define the query() method in ResourceList class as such:

The query method is executed for GET requests, so this if clause looks for track_id in view_kwargs dict. When the request is made to `/v1/tracks/<int:track_id>/sessions`, track id will be present as ‘track_id in the view_kwargs. The tracks are filtered based on the id passed here and then joined on the query with all sessions object from database.

For the POST method, we need to add the track_id from view_kwargs to pass into the track_id field of database model. This is achieved by using flask-rest-jsonapi’s before_create_object() method. The implementation for track_id is the following:

When a POST request is made to `/v1/tracks/<int:track_id>/sessions`, the view_kwargs dict will have ‘track_id’ in it. So if track_id is present in the url params, we first ensure that a track with the passed id exists, then only proceed to create a sessions object under the given track. Now the safe_query() method is a generic custom method written to check for such things. The model is passed along with the filter attribute, and a field to include in the error message. This method throws an ObjectNotFound exception if no such object exists for a given id.

We also need to take care of the permissions for these endpoints. As the decorators are called even before schema validation, it was difficult to get the event_id for permissions unless adding highly endpoint-specific code in the permission manager core, leading to loss of generality.  So the leave_if parameter of permission check was used to overcome this issue. Since the permissions manager isn’t fully developed yet, this is to be changed in the improved implementation of the permissions manager.

Similar implementations for micro locations and session types was done. All the same is not explained in this blogpost. For extended code, take a look at the source code of this schema.

For speaker relation, a few things were different. This is because speakers-sessions is a many-to-many relationship. Let’s take a look at this:

As it is a many-to-many relationship, a association_table was used with flask-sqlalchemy. So for the query() method, the same association table is queried after extracting the speaker_id from view_kwargs dict.

For the POST request on `/v1/speakers/<int:speaker_id>/sessions` , flask-rest-jsonapi’s after_create_object() method is used to insert the request in association_table. In this method the parameters are the following: self, obj, data, view_kwargs

Now view_kwargs contains the url parameters, so we make a check for speaker_id in view_wargs. If it is present, then before proceeding to insert data, we ensure that a speaker exists with that id using the safe_query() method as described above. After that, the obj argument of the method is used. This contains the object that was created in  previous method. So now once the sessions object has been created and we are sure that a speaker exists with given speaker_id, this is just to be appended to obj.speakers  so that this relationship tuple is inserted into the association table.

This updates the association table ‘speakers_session’ in this case. The other such endpoints are being worked upon in a similar fashion and will be consolidated as part of a set of robust APIs, along with the improved permissions manager for the Open Event API Server.

Additional Resources

Code, Issues and Pull Request involved

Continue Reading
Close Menu