Работа с плагинами

Подключение плагинов

Плагины подключаются в момент инициализации JSON API. Рассмотрим пример подключения плагина EventPlugin

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

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

API для плагинов

При реализации плагина доступны следующие hooks

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

срабатывает перед инициализацией json_api

  • app - ссылка на объект приложения Flask.

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

срабатывает после инициализации json_api

  • app - ссылка на объект приложения Flask.

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

Предобработка ресурс менеджеров до создания роутеров

  • resource - ресурс менеджер;

  • view - название ресурс менеджера;

  • urls - список url, по которым будет доступен данный ресурс;

  • self_json_api - ссылка на объект Api.

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

Постобработка ресурс менеджеров после создания роутеров

  • resource - ресурс менеджер;

  • view - название ресурс менеджера;

  • urls - список url, по которым будет доступен данный ресурс;

  • self_json_api - ссылка на объект Api.

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

Выполняется после инициализация marshmallow схемы в ResourceList.post

  • schema - схема, которая привязана к ресурсу для сериализации/десериализации;

  • model - модель, которая привязана к ресурсу.

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

Выполняется после инициализация marshmallow схемы в ResourceList.get

  • schema - схема, которая привязана к ресурсу для сериализации/десериализации;

  • model - модель, которая привязана к ресурсу.

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

Выполняется после инициализация marshmallow схемы в ResourceDetail.get

  • schema - схема, которая привязана к ресурсу для сериализации/десериализации;

  • model - модель, которая привязана к ресурсу.

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

Выполняется после инициализация marshmallow схемы в ResourceDetail.patch

  • schema - схема, которая привязана к ресурсу для сериализации/десериализации;

  • model - модель, которая привязана к ресурсу.

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

Выполняется после десериализации данных и до создания запроса к бд на создание нового объекта

  • data - десериализованнаые данные для создания объекта;

  • view_kwargs - kwargs из ресурс менеджера;

  • self_json_api - ссылка на объект Api.

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

Обрабатывает данные, которые пойдут непосредственно на создание нового объекта. Возвращает обновлённый набор данных для нового объекта

  • Dict data - сырые данные, на основе которых будет создан новый объект;

  • view_kwargs - kwargs из ресурс менеджера;

  • List[str] join_fields - список полей, которые являются ссылками на другие модели;

  • self_json_api - ссылка на объект Api.

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

Выполняется после создание нового объекта, но до сохранения в БД

  • Dict data - данные, использованные для создания нового объекта;

  • view_kwargs - kwargs из ресурс менеджера;

  • obj - новый объект, созданный на основе data;

  • self_json_api - ссылка на объект Api.

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

Вызывается во время создания запроса к БД на обновление объекта. Тут можно пропатчить запрос к БД. Возвращает пропатченный запрос к бд

  • Query query - сформированный запрос к БД;

  • QueryStringManager qs - список параметров для запроса;

  • view_kwargs - kwargs из ресурс менеджера;

  • self_json_api - ссылка на объект Api.

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

Во время создания запроса к БД на обновление объектов. Тут можно пропатчить запрос к БД. Возвращает пропатченный запрос к бд

  • Query query - сформированный запрос к БД;

  • QueryStringManager qs - список параметров для запроса;

  • view_kwargs - kwargs из ресурс менеджера;

  • self_json_api - ссылка на объект Api.

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

Обрабатывает данные, которые пойдут непосредственно на обновления объекта. Возвращает обновлённый набор данных data для обновления объекта

  • Dict data - данные, которыми будет обновлён объект;

  • obj - редактируемый объект;

  • view_kwargs - kwargs из ресурс менеджера;

  • self_json_api - ссылка на объект Api.

  • List[str] join_fields - список полей, которые являются ссылками на другие модели.

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

Выполняется до удаления объекта из БД

  • obj - удаляемый объект;

  • view_kwargs - kwargs из ресурс менеджера;

  • self_json_api - ссылка на объект Api.

before_data_layers_filtering_alchemy_nested_resolve(self, self_nested: Any) -> None

Вызывается до создания фильтра в функции Nested.resolve, если после выполнения вернёт None, то дальше продолжится работа функции resolve, если вернёт какое либо значения отличное от None, то функция resolve завершается, а результат hook функции передаётся дальше в стеке вызова.

  • self_nested - экземпляр Nested.

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.

Пример создания плагинов

Рассмотрим пример реализации плагина, который будет отдавать данные в get запросах для ResourceList, ResourceDetail в двух вариантах либо все, либо укороченные по заранее заданному параметру 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)