filterable


Namefilterable JSON
Version 0.0.3 PyPI version JSON
download
home_pagehttps://github.com/totvslabs/filterable
SummaryFilterable handler to handle with request filters
upload_time2023-09-05 14:41:54
maintainer
docs_urlNone
authorTotvsLabs
requires_python>=3.7
license
keywords pagination assistant handler helper filter request args paginate filters filtering
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            Filterable
![PyPI - Version](https://img.shields.io/pypi/v/filterable?color=blue)
![PyPI - Dependencies](https://img.shields.io/librariesio/release/pypi/filterable?color=green)
![PyPI - Implementation](https://img.shields.io/pypi/implementation/filterable?color=purple)
=======
**Standardize filters and pagination in your Python API**

Filterable is a Python library that simplifies the application of filters, pagination, and sorting to SQLAlchemy queries based on HTTP requests. It provides a convenient way to process request parameters and apply these filters and sorts to SQLAlchemy queries, enabling standardization of filters used by client integrations with your Python API across all routes.

Anchors:
- [Dependencies](#dependencies)
- [Introduction](#introduction)
- [Installation](#installation)
- [Filters](#filters)
    - [Default schema](#default-schema)
    - [Filtering data](#filtering-data)
    - [Filter example usage](#filter-example-usage)
    - [Query engine](#query-engine)
- [Sorting](#sorting)
    - [Sort example usage](#sort-example-usage)
- [The `Filterable` class](#the-filterable-class)
    - [Handling Pagination](#handling-pagination)
      

## Dependencies
- [Python](https://www.python.org/) >= 3.7
- [SqlAlchemy](https://github.com/sqlalchemy/sqlalchemy) >= 2.0

## Introduction
Filterable is a class decorator method that applies filters to SQLAlchemy queries based on the arguments passed in the request. Simply decorate your method with Filterable, and it will inject a dictionary called `_filtered` with the applied filters, sorting, and pagination.

```python
from filterable import Filterable
from your_app.models import UserModel

class YourController():

    @Filterable(UserModel)
    def get_users(self, _filtered):
        users_query = session().query(UserModel).filter(
            _filtered.filter
        ).order_by(*_filtered.sort)

        return users_query.all(), 200
```

## Installation
You can install it using pip:
```
pip install filterable
```

## Filters
Requests must follow the following pattern:

```url
https://your-aplication.com?page=1&pageSize=10&sort=GENERIC_REQUEST_SORT&filters=GENERIC_REQUEST_FILTER
```

### Default Schema
```javascript
{
    /** default: 0 **/
    /** noLimit: -1, optional support to return all entities **/
    "page"?: 1

    /** default: 25 **/
    "pageSize"?: 10,

    /** optional **/
    /** description: sorting by one or multiple criteria **/
    "sort"?: GENERIC_REQUEST_SORT | GENERIC_REQUEST_SORT[],

    /** optional **/
    /** description: multiple filters list **/
    "filters":? GENERIC_REQUEST_FILTER[],

    /** your endpoint custom args **/
    ** : **
}
```
> NOTE: Below is more information about the GENERIC_REQUEST_SORT and GENERIC_REQUEST_FILTER schema for sorting and filters.

### Filtering Data
#### `[GENERIC_REQUEST_FILTER]`
In the filters attribute of the JSON request object, you define the conditions that will be applied to the SQLAlchemy query. This is the standard structure of a filter:

```javascript
{
    /** description: field to filter by **/
    "f": string,

    /** description: operation to be applied **/
    /** default: 'eq' **/
    "o"?:
        | 'eq'   // eq  : equals
        | 'neq'  // neq : not equals
        | 'gt'   // gt  : greater than
        | 'gte'  // gte : greater than or equal
        | 'lt'   // lt  : lower than
        | 'lte'  // lte : lower than or equal
        | 'in'   // in  : any value in a given list (value is a list)
        | 'nin'  // nin : any value not in a given list (value is a list)
        | 'has'  // has : list has a given value (for entity list attributes )
        | 'hasn' // hasn: list has not a given value (for entity list attributes )
        | 'lk'   // lk  : like or contains (may support widlcard '%')
        | 'nlk'  // nlk : not like or contains (may support widlcard '%')
        | 'btw'  // btw : value between two given values (value is a tuple [min, max]),    	

    /** description: value to filter by - depends on field type and operation **/
    "v": unknown,
}
```

### Filter example usage:

Basic usage:
```url
https://your-aplication.com?filters=[{"f":"first_name","o":"eq","v": "Steve"}]
```

Filter schema:
```json
[
    { "f":"first_name", "o":"eq", "v": "Steve" }
]
```

Multiple filters:
```json
[
    { "f":"name", "o":"eq", "v": "Steve" },
    { "f":"code", "o":"eq", "v": 55 }
]
```

Specific filter types:
```json
[
    { "f":"first_name", "o":"eq",  "v": "Steve" },
    { "f":"last_name",  "o":"neq", "v": "Jobs" },
    { "f":"age",        "o":"btw", "v": [10, 50] },
    { "f":"city",       "o":"in",  "v": ["São Paulo", "New York"] }
]
```

### Query Engine:

A request like this:
```url
    ?filter=[
        {f:'fieldA', o:'eq' ,v:10},
        {f:'fieldB', o:'lt' ,v:100},
        {f:'fieldC', o:'btw',v:[50,100]},
        {f:'fieldD', o:'lk' ,v:'del%'},
        {f:'fieldE', o:'in' ,v:['BRT','MX','USA']},
    ]
```

Internally produces a SQL query like this:
```sql
    WHERE fieldA = 10
        AND fieldB < 100
        AND fieldC BETWEEN 50 AND 100
        AND fieldD LIKE 'del%'
        AND fieldE IN ('BRT','MX','USA')
```


## Sorting
#### [GENERIC_REQUEST_SORT]
Just like the filter attribute to define query conditions, you can also send the sort attribute to define the order of the query result.

```javascript
{
    /** description: field to filter by **/
    "f": string,

    /** description: operation to be applied **/
    /** default: 'eq' **/
    "o"?:
        | 'asc'   // ascending sort
        | 'desc'  // desceding sort
}
```

### Sort example usage:

Basic usage:
```url
https://your-aplication.com?sort=[{"f":"name"}]
```

Sort schema:
```json
[
    { "f":"name" }
]
```
> NOTE: Since the o attribute was not passed, it will assume the default ascending sorting (asc).

Multiple sorting:
```json
[
    { "f":"name",      "o":"asc" },
    { "f":"birthday",  "o":"desc" },
    { "f":"last_seen", "o":"asc" },
]
```

## The `Filterable` Class
When decorating your class method with Filterable, you need to provide the respective Model for the query:

```python
from filterable import Filterable
from your_app.models import YourModel

class YourClass:
    @Filterable(YourModel)
    def get_users(self, _filtered):
        pass
```

Notice that the _filtered parameter has been injected into the decorated method. This parameter is of type dict and contains the queries and sorting created according to the arguments sent in the request to your API. Now you just need to incorporate it into your SQLAlchemy query:

```python
    @Filterable(YourModel)
    def get_users(self, _filtered):
        session = Session()
        users_query = self.session.query(YourModel).filter(
            _filtered.filter,
            YourModel.deleted === False
        ).order_by(*_filtered.sort)

        return users_query.all(), 200
```
> NOTE: You can still add other custom conditions to the query while incorporating the conditions that Filterable created.
> NOTE: It is necessary to unpack (*) the sorting data.

### Handling Pagination
Additionally, `Filterable` supports pagination. It handles and applies the pagination information received in the request and returns a pattern that is easy to implement for front-end clients.

Example Usage:
```python
from filterable import Filterable, paginate
from your_app.models import YourModel

class YourClass:
    @Filterable(YourModel)
    def get_users(self, _filtered):
        users_query = session().query(YourModel).filter(
            _filtered.filter,
            YourModel.deleted === False
        ).order_by(*_filtered.sort)

        return paginate(users_query), 200
```

When making a call to this endpoint, you will receive the following response:

```json
{
    "page": 1,
    "pageSize": 15,
    "rows": [],
    "totalRows": 265,
}
```

Summary:
| Parameter | Type | Description |
|-----------|------|-----------|
| page | ``int`` | Current page |
| pageSize | ``int`` | Number of items displayed per page |
| rows | ``list`` | List of entities (models) returned in the query |
| totalRows | ``int`` | Total number of items that this query returns |

## License
MIT (LICENSE)

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/totvslabs/filterable",
    "name": "filterable",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.7",
    "maintainer_email": "",
    "keywords": "pagination,assistant,handler,helper,filter,request,args,paginate,filters,filtering",
    "author": "TotvsLabs",
    "author_email": "info@totvslabs.com",
    "download_url": "https://files.pythonhosted.org/packages/a3/44/df3752f8e21cf73074106681d32242273b2db280472b14d1e79d6083bc43/filterable-0.0.3.tar.gz",
    "platform": null,
    "description": "Filterable\n![PyPI - Version](https://img.shields.io/pypi/v/filterable?color=blue)\n![PyPI - Dependencies](https://img.shields.io/librariesio/release/pypi/filterable?color=green)\n![PyPI - Implementation](https://img.shields.io/pypi/implementation/filterable?color=purple)\n=======\n**Standardize filters and pagination in your Python API**\n\nFilterable is a Python library that simplifies the application of filters, pagination, and sorting to SQLAlchemy queries based on HTTP requests. It provides a convenient way to process request parameters and apply these filters and sorts to SQLAlchemy queries, enabling standardization of filters used by client integrations with your Python API across all routes.\n\nAnchors:\n- [Dependencies](#dependencies)\n- [Introduction](#introduction)\n- [Installation](#installation)\n- [Filters](#filters)\n    - [Default schema](#default-schema)\n    - [Filtering data](#filtering-data)\n    - [Filter example usage](#filter-example-usage)\n    - [Query engine](#query-engine)\n- [Sorting](#sorting)\n    - [Sort example usage](#sort-example-usage)\n- [The `Filterable` class](#the-filterable-class)\n    - [Handling Pagination](#handling-pagination)\n      \n\n## Dependencies\n- [Python](https://www.python.org/) >= 3.7\n- [SqlAlchemy](https://github.com/sqlalchemy/sqlalchemy) >= 2.0\n\n## Introduction\nFilterable is a class decorator method that applies filters to SQLAlchemy queries based on the arguments passed in the request. Simply decorate your method with Filterable, and it will inject a dictionary called `_filtered` with the applied filters, sorting, and pagination.\n\n```python\nfrom filterable import Filterable\nfrom your_app.models import UserModel\n\nclass YourController():\n\n    @Filterable(UserModel)\n    def get_users(self, _filtered):\n        users_query = session().query(UserModel).filter(\n            _filtered.filter\n        ).order_by(*_filtered.sort)\n\n        return users_query.all(), 200\n```\n\n## Installation\nYou can install it using pip:\n```\npip install filterable\n```\n\n## Filters\nRequests must follow the following pattern:\n\n```url\nhttps://your-aplication.com?page=1&pageSize=10&sort=GENERIC_REQUEST_SORT&filters=GENERIC_REQUEST_FILTER\n```\n\n### Default Schema\n```javascript\n{\n    /** default: 0 **/\n    /** noLimit: -1, optional support to return all entities **/\n    \"page\"?: 1\n\n    /** default: 25 **/\n    \"pageSize\"?: 10,\n\n    /** optional **/\n    /** description: sorting by one or multiple criteria **/\n    \"sort\"?: GENERIC_REQUEST_SORT | GENERIC_REQUEST_SORT[],\n\n    /** optional **/\n    /** description: multiple filters list **/\n    \"filters\":? GENERIC_REQUEST_FILTER[],\n\n    /** your endpoint custom args **/\n    ** : **\n}\n```\n> NOTE: Below is more information about the GENERIC_REQUEST_SORT and GENERIC_REQUEST_FILTER schema for sorting and filters.\n\n### Filtering Data\n#### `[GENERIC_REQUEST_FILTER]`\nIn the filters attribute of the JSON request object, you define the conditions that will be applied to the SQLAlchemy query. This is the standard structure of a filter:\n\n```javascript\n{\n    /** description: field to filter by **/\n    \"f\": string,\n\n    /** description: operation to be applied **/\n    /** default: 'eq' **/\n    \"o\"?:\n        | 'eq'   // eq  : equals\n        | 'neq'  // neq : not equals\n        | 'gt'   // gt  : greater than\n        | 'gte'  // gte : greater than or equal\n        | 'lt'   // lt  : lower than\n        | 'lte'  // lte : lower than or equal\n        | 'in'   // in  : any value in a given list (value is a list)\n        | 'nin'  // nin : any value not in a given list (value is a list)\n        | 'has'  // has : list has a given value (for entity list attributes )\n        | 'hasn' // hasn: list has not a given value (for entity list attributes )\n        | 'lk'   // lk  : like or contains (may support widlcard '%')\n        | 'nlk'  // nlk : not like or contains (may support widlcard '%')\n        | 'btw'  // btw : value between two given values (value is a tuple [min, max]),    \t\n\n    /** description: value to filter by - depends on field type and operation **/\n    \"v\": unknown,\n}\n```\n\n### Filter example usage:\n\nBasic usage:\n```url\nhttps://your-aplication.com?filters=[{\"f\":\"first_name\",\"o\":\"eq\",\"v\": \"Steve\"}]\n```\n\nFilter schema:\n```json\n[\n    { \"f\":\"first_name\", \"o\":\"eq\", \"v\": \"Steve\" }\n]\n```\n\nMultiple filters:\n```json\n[\n    { \"f\":\"name\", \"o\":\"eq\", \"v\": \"Steve\" },\n    { \"f\":\"code\", \"o\":\"eq\", \"v\": 55 }\n]\n```\n\nSpecific filter types:\n```json\n[\n    { \"f\":\"first_name\", \"o\":\"eq\",  \"v\": \"Steve\" },\n    { \"f\":\"last_name\",  \"o\":\"neq\", \"v\": \"Jobs\" },\n    { \"f\":\"age\",        \"o\":\"btw\", \"v\": [10, 50] },\n    { \"f\":\"city\",       \"o\":\"in\",  \"v\": [\"S\u00e3o Paulo\", \"New York\"] }\n]\n```\n\n### Query Engine:\n\nA request like this:\n```url\n    ?filter=[\n        {f:'fieldA', o:'eq' ,v:10},\n        {f:'fieldB', o:'lt' ,v:100},\n        {f:'fieldC', o:'btw',v:[50,100]},\n        {f:'fieldD', o:'lk' ,v:'del%'},\n        {f:'fieldE', o:'in' ,v:['BRT','MX','USA']},\n    ]\n```\n\nInternally produces a SQL query like this:\n```sql\n    WHERE fieldA = 10\n        AND fieldB < 100\n        AND fieldC BETWEEN 50 AND 100\n        AND fieldD LIKE 'del%'\n        AND fieldE IN ('BRT','MX','USA')\n```\n\n\n## Sorting\n#### [GENERIC_REQUEST_SORT]\nJust like the filter attribute to define query conditions, you can also send the sort attribute to define the order of the query result.\n\n```javascript\n{\n    /** description: field to filter by **/\n    \"f\": string,\n\n    /** description: operation to be applied **/\n    /** default: 'eq' **/\n    \"o\"?:\n        | 'asc'   // ascending sort\n        | 'desc'  // desceding sort\n}\n```\n\n### Sort example usage:\n\nBasic usage:\n```url\nhttps://your-aplication.com?sort=[{\"f\":\"name\"}]\n```\n\nSort schema:\n```json\n[\n    { \"f\":\"name\" }\n]\n```\n> NOTE: Since the o attribute was not passed, it will assume the default ascending sorting (asc).\n\nMultiple sorting:\n```json\n[\n    { \"f\":\"name\",      \"o\":\"asc\" },\n    { \"f\":\"birthday\",  \"o\":\"desc\" },\n    { \"f\":\"last_seen\", \"o\":\"asc\" },\n]\n```\n\n## The `Filterable` Class\nWhen decorating your class method with Filterable, you need to provide the respective Model for the query:\n\n```python\nfrom filterable import Filterable\nfrom your_app.models import YourModel\n\nclass YourClass:\n    @Filterable(YourModel)\n    def get_users(self, _filtered):\n        pass\n```\n\nNotice that the _filtered parameter has been injected into the decorated method. This parameter is of type dict and contains the queries and sorting created according to the arguments sent in the request to your API. Now you just need to incorporate it into your SQLAlchemy query:\n\n```python\n    @Filterable(YourModel)\n    def get_users(self, _filtered):\n        session = Session()\n        users_query = self.session.query(YourModel).filter(\n            _filtered.filter,\n            YourModel.deleted === False\n        ).order_by(*_filtered.sort)\n\n        return users_query.all(), 200\n```\n> NOTE: You can still add other custom conditions to the query while incorporating the conditions that Filterable created.\n> NOTE: It is necessary to unpack (*) the sorting data.\n\n### Handling Pagination\nAdditionally, `Filterable` supports pagination. It handles and applies the pagination information received in the request and returns a pattern that is easy to implement for front-end clients.\n\nExample Usage:\n```python\nfrom filterable import Filterable, paginate\nfrom your_app.models import YourModel\n\nclass YourClass:\n    @Filterable(YourModel)\n    def get_users(self, _filtered):\n        users_query = session().query(YourModel).filter(\n            _filtered.filter,\n            YourModel.deleted === False\n        ).order_by(*_filtered.sort)\n\n        return paginate(users_query), 200\n```\n\nWhen making a call to this endpoint, you will receive the following response:\n\n```json\n{\n    \"page\": 1,\n    \"pageSize\": 15,\n    \"rows\": [],\n    \"totalRows\": 265,\n}\n```\n\nSummary:\n| Parameter | Type | Description |\n|-----------|------|-----------|\n| page | ``int`` | Current page |\n| pageSize | ``int`` | Number of items displayed per page |\n| rows | ``list`` | List of entities (models) returned in the query |\n| totalRows | ``int`` | Total number of items that this query returns |\n\n## License\nMIT (LICENSE)\n",
    "bugtrack_url": null,
    "license": "",
    "summary": "Filterable handler to handle with request filters",
    "version": "0.0.3",
    "project_urls": {
        "Homepage": "https://github.com/totvslabs/filterable"
    },
    "split_keywords": [
        "pagination",
        "assistant",
        "handler",
        "helper",
        "filter",
        "request",
        "args",
        "paginate",
        "filters",
        "filtering"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "a344df3752f8e21cf73074106681d32242273b2db280472b14d1e79d6083bc43",
                "md5": "e3b9cee729f7c434448ad9abebbae08f",
                "sha256": "a6e588daa5e849fb08f1a65ba00a095ae42d0ffa528d71734871ce3237a0d53f"
            },
            "downloads": -1,
            "filename": "filterable-0.0.3.tar.gz",
            "has_sig": false,
            "md5_digest": "e3b9cee729f7c434448ad9abebbae08f",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.7",
            "size": 11808,
            "upload_time": "2023-09-05T14:41:54",
            "upload_time_iso_8601": "2023-09-05T14:41:54.887997Z",
            "url": "https://files.pythonhosted.org/packages/a3/44/df3752f8e21cf73074106681d32242273b2db280472b14d1e79d6083bc43/filterable-0.0.3.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-09-05 14:41:54",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "totvslabs",
    "github_project": "filterable",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "requirements": [],
    "lcname": "filterable"
}
        
Elapsed time: 0.11192s