Плагин PostgreSqlJSONB

Плагин PostgreSqlJSONB позволяет:

  1. Работать с типом полей JSONB в PostgreSql как с обычной моделью в плане выгрузки на клиент. Работает это для get запросов в фильтрации и сортировке. Работать можно с полями первого уровня.

  2. Интегрируется с плагином ApiSpecPlugin в swagger в get запросах (когда выгружается ResourceList). Появились доп. поля:

    • filter[<название JSONB поля в модели>.<название поля верхнего уровня в JSONB>] - обычные фильтры

    • filter = [{"name": "<название JSONB поля в модели>__<название\поля верхнего уровня в JSONB>", "op": "eq", "val": "<значение>"}] - в составных фильтрах также обращаемся к полям внутри JSONB поля, как к полям другой модели.

    • sort=<название JSONB поля в модели>.<название поля верхнего уровня в JSONB> - в сортировке используется как глубокая сортировка.

  3. Интегрируется с плагином PermissionPlugin, можно в пермишен кейсах описать ограничения на поля верхнего уровня в поле модели JSONB.

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

Чтобы интегрировать плагин в свои схемы, в которых описаны модели с полями JSONB, нужно сделать следующее:

  1. В схеме описываем поле JSONB (из модели) как Nested, на схему со структурой того, что планируется хранить в JSONB

  2. Схема созданная в первом пункте для хранения структуры из поля модели JSONB должно наследоваться от класса combojsonapi.postgresql_jsonb.schema.SchemaJSONB

И всё :)

Пример подключения плагина

Рассмотрим пример, когда в поле JSONB будем хранить настройки пользователя. Пример будет работать только с подключение к базе данных postgresql

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import Column, Integer, String
from sqlalchemy.dialects.postgresql.json import JSONB
from sqlalchemy.orm import Query, load_only, scoped_session
from flask_combo_jsonapi.marshmallow_fields import Relationship
from flask_combo_jsonapi import Api, ResourceList, ResourceDetail
from flask_combo_jsonapi.querystring import QueryStringManager
from combojsonapi.postgresql_jsonb.schema import SchemaJSONB
from combojsonapi.postgresql_jsonb import PostgreSqlJSONB
from combojsonapi.spec import ApiSpecPlugin
from marshmallow_jsonapi.flask import Schema
from marshmallow_jsonapi import fields


app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = '<подключение к Postgresql базе данных>'
app.config['SQLALCHEMY_ECHO'] = True
db = SQLAlchemy(app)

"""Models description"""

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


db.create_all()

"""Models' schemas description"""

class SettingsSchema(SchemaJSONB):
    active = fields.Boolean()
    age = fields.Integer()

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()
    settings = fields.Nested('SettingsSchema')

"""API resource managers description"""

class UserResourceDetail(ResourceDetail):
    schema = UserSchema
    events = UserEventsForResourceDetail
    methods = ['GET']
    data_layer = {
        'session': db.session,
        'model': User,
    }

class UserResourceList(ResourceList):
    schema = UserSchema
    methods = ['GET', 'POST']
    data_layer = {
        'session': db.session,
        'model': User,
    }

"""Initializing the API"""

app.config['OPENAPI_URL_PREFIX'] = '/api/swagger'
app.config['OPENAPI_SWAGGER_UI_PATH'] = '/'
app.config['OPENAPI_SWAGGER_UI_VERSION'] = '3.22.0'

api_spec_plagin = ApiSpecPlugin(
    app=app,
    # Declaring tags list with their descriptions, so API gets organized into groups. This is optional: when there's no tags,
    # api will be grouped automatically by type schemas names (type_)
    tags={
        'User': 'API для user'
    }
)

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


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(port='9999')

Пример использования в запросах

С views, описанными в примере выше, мы можем пользоваться новыми фильтрами и сортировкой.

Запрос всех активных пользователей простым фильтром:

/api/user/?filter[settings.active]=True

Запрос всех совершеннолетних пользователей (сложный фильтр), сортированных по возрасту по убыванию, затем по имени по возрастанию:

/api/user/?filter=[{"name":"settings.age","op": "gt","val": "18"}]&sort=-settings.age,name