Using Plugins

Attaching Plugins

Plugins are attached with JSON API initialization. Here’s a example of EventPlugin usage:

from flask import Flask
from flask_combo_jsonapi import Api
from combojsonapi.event import EventPlugin

app = Flask(__name__)
api_json = Api(
    app,
    plugins=[
        EventPlugin(),
    ]
)

Plugins API

Following hooks are available at plugin initialization

before_init_plugin(self, *args, app=None, **kwargs) -> None

Fired before json_api initializes

  • app - link to Flask instance object

after_init_plugin(self, *args, app=None, **kwargs) -> None

Fired after json_api initializes

  • app - link to Flask instance object

before_route(self, resource: Union[ResourceList, ResourceDetail] = None, view=None, urls: Tuple[str] = None, self_json_api: Api = None, **kwargs) -> None:

Resource managers pre-parsing before routers are created

  • resource - resource manager;

  • view - resource manager name;

  • urls - URLs list at which resource will be available;

  • self_json_api - link to Api instance.

after_route(self, resource: Union[ResourceList, ResourceDetail] = None, view=None, urls: Tuple[str] = None, self_json_api: Api = None, **kwargs) -> None:

Resource managers post-parsing after routers are created

  • resource - resource manager;

  • view - resource manager name;

  • urls - URLs list at which resource will be available;

  • self_json_api - link to Api instance.

after_init_schema_in_resource_list_post(self, *args, schema=None, model=None, **kwargs) -> None

Called after marshmallow schema initialization in ResourceList.post

  • schema - serialization/deserialization schema linked with the resource;

  • model - model linked with the resource.

after_init_schema_in_resource_list_get(self, *args, schema=None, model=None, **kwargs) -> None

Called after marshmallow schema initialization in ResourceList.get

  • schema - serialization/deserialization schema linked with the resource;

  • model - model linked with the resource.

after_init_schema_in_resource_detail_get(self, *args, schema=None, model=None, **kwargs) -> None

Called after marshmallow schema initialization in ResourceDetail.get

  • schema - serialization/deserialization schema linked with the resource;

  • model - model linked with the resource.

after_init_schema_in_resource_detail_patch(self, *args, schema=None, model=None, **kwargs) -> None

Called after marshmallow schema initialization in ResourceDetail.patch

  • schema - serialization/deserialization schema linked with the resource;

  • model - model linked with the resource.

data_layer_before_create_object(self, *args, data=None, view_kwargs=None, self_json_api=None, **kwargs) -> None

Called after data deserialization and before forming a database request to create a new object

  • data - deserialized data;

  • view_kwargs - resource manager kwargs;

  • self_json_api - link to Api instance.

data_layer_create_object_clean_data(self, *args, data: Dict = None, view_kwargs=None, join_fields: List[str] = None, self_json_api=None, **kwargs) -> Dict

Parses input data and returns parsed data set, from which a new object will be created.

  • Dict data - deserialized unparsed data set;

  • view_kwargs - resource manager kwargs;

  • List[str] join_fields - fields which are linked to other models;

  • self_json_api - link to Api instance.

data_layer_after_create_object(self, *args, data=None, view_kwargs=None, self_json_api=None, obj=None, **kwargs) -> None

Called after object creation but before saving it to the database.

  • Dict data - data used to create the new object;

  • view_kwargs - resource manager kwargs;

  • obj - object created from data;

  • self_json_api - link to Api instance.

data_layer_get_object_update_query(self, *args, query: Query = None, qs: QueryStringManager = None, view_kwargs=None, self_json_api=None, **kwargs) -> Query

Called during database query creation for updating a single object. Query can be patched here, if needed. Returns patched DB query.

  • Query query - generated database query;

  • QueryStringManager qs - query parameters list;

  • view_kwargs - resource manager kwargs;

  • self_json_api - link to Api instance.

data_layer_get_collection_update_query(self, *args, query: Query = None, qs: QueryStringManager = None, view_kwargs=None, self_json_api=None, **kwargs) -> Query

Called during database query creation for updating multiple objects. Query can be patched here, if needed. Returns patched DB query.

  • Query query - generated database query;

  • QueryStringManager qs - query parameters list;

  • view_kwargs - resource manager kwargs;

  • self_json_api - link to Api instance.

data_layer_update_object_clean_data(self, *args, data: Dict = None, obj=None, view_kwargs=None, join_fields: List[str] = None, self_json_api=None, **kwargs) -> Dict

Parses data for the object to be updated. Returns parsed data set.

  • Dict data - data with which the object is to be updated;

  • obj - object to be updated;

  • view_kwargs - resource manager kwargs;

  • self_json_api - link to Api instance.

  • List[str] join_fields - fields which are linked to other models.

data_layer_delete_object_clean_data(self, *args, obj=None, view_kwargs=None, self_json_api=None, **kwargs) -> None

Called before deleting object from the database.

  • obj - object to delete;

  • view_kwargs - resource manager kwargs;

  • self_json_api - link to Api instance.

before_data_layers_filtering_alchemy_nested_resolve(self, self_nested: Any) -> None

Called before filter is created in Nested.resolve. When returns None, resolve continues executing; when returns any other value, resolve exits, and the hook function result is passed further in the call stack.

  • self_nested - Nested instance.

before_data_layers_sorting_alchemy_nested_resolve(self, self_nested: Any) -> None

Called before sort is created in Nested.resolve. When returns None, resolve continues executing; when returns any other value, resolve exits, and the hook function result is passed further in the call stack.

  • self_nested - Nested instance.

Making a New Plugin Example

Let’s take a look at example implementation of a plugin that will return data from get requests to ResourceList, ResourceDetail in a short or detailed view based on pre-set parameter format=short|full

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import Column, Integer, String
from sqlalchemy.orm import Query, load_only, scoped_session
from combojsonapi.utils import Relationship
from flask_combo_jsonapi import Api, ResourceList, ResourceDetail
from flask_combo_jsonapi.plugin import BasePlugin
from flask_combo_jsonapi.querystring import QueryStringManager
from marshmallow_jsonapi.flask import Schema
from marshmallow_jsonapi import fields


app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///:memory:'
app.config['SQLALCHEMY_ECHO'] = True
db = SQLAlchemy(app)
app.config['FLASK_DEBUG'] = 1


class User(db.Model):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    name = Column(String)
    fullname = Column(String)
    email = Column(String)
    password = Column(String)


db.create_all()


class UserSchema(Schema):
    class Meta:
        type_ = 'user'
        self_view = 'user_detail'
        self_view_kwargs = {'id': '<id>'}
        self_view_many = 'user_list'
        ordered = True

    id = fields.Integer(as_string=True)
    name = fields.String()
    fullname = fields.String()
    email = fields.String()
    password = fields.String()


class UserResourceList(ResourceList):
    schema = UserSchema
    method = ['GET']
    data_layer = {
        'session': db.session,
        'model': User,
        'short_format': ['id', 'name']
    }


class UserResourceDetail(ResourceDetail):
    schema = UserSchema
    method = ['GET']
    data_layer = {
        'session': db.session,
        'model': User,
        'short_format': ['id', 'name']
    }


class FormatPlugin(BasePlugin):

    def _update_query(self, *args, query: Query = None, qs: QueryStringManager = None,
                        view_kwargs=None, self_json_api=None, **kwargs) -> Query:
        all_fields = self_json_api.model.__mapper__.column_attrs.keys()
        short_format = self_json_api.short_format if hasattr(self_json_api, 'short_format') else all_fields
        full_format = self_json_api.full_format if hasattr(self_json_api, 'full_format') else all_fields
        fields = short_format if qs.qs.get('format') == 'short' else full_format

        query = self_json_api.session.query(*[getattr(self_json_api.model, name_field) for name_field in  fields])
        return query

    def data_layer_get_object_update_query(self, *args, query: Query = None, qs: QueryStringManager = None,
                                            view_kwargs=None, self_json_api=None, **kwargs) -> Query:
        return self._update_query(*args, query=query, qs=qs, view_kwargs=view_kwargs,
                                    self_json_api=self_json_api, **kwargs)

    def data_layer_get_collection_update_query(self, *args, query: Query = None, qs: QueryStringManager = None,
                                                view_kwargs=None, self_json_api=None, **kwargs) -> Query:
        return self._update_query(*args, query=query, qs=qs, view_kwargs=view_kwargs,
                                    self_json_api=self_json_api, **kwargs)



api_json = Api(
    app,
    plugins=[
        FormatPlugin(),
    ]
)
api_json.route(UserResourceList, 'user_list', '/api/user/')
api_json.route(UserResourceDetail, 'user_detail', '/api/user/<int:id>/')


if __name__ == '__main__':
    for i in range(10):
        u = User(name=f'name{i}', fullname=f'fullname{i}', email=f'email{i}', password=f'password{i}')
        db.session.add(u)
    db.session.commit()
    app.run(use_reloader=True)