https://github.com/demoskp/flask-query-builder
A query builder for flask and sqlalchemy
https://github.com/demoskp/flask-query-builder
Last synced: 6 months ago
JSON representation
A query builder for flask and sqlalchemy
- Host: GitHub
- URL: https://github.com/demoskp/flask-query-builder
- Owner: demoskp
- License: mit
- Created: 2022-07-09T12:30:36.000Z (almost 3 years ago)
- Default Branch: master
- Last Pushed: 2024-03-22T19:33:26.000Z (about 1 year ago)
- Last Synced: 2024-11-04T19:52:52.254Z (7 months ago)
- Language: Python
- Size: 44.9 KB
- Stars: 9
- Watchers: 1
- Forks: 2
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
Awesome Lists containing this project
- jimsghstars - demoskp/flask-query-builder - A query builder for flask and sqlalchemy (Python)
README
# Build SQLAlchemy queries from API requests


[](https://pypi.python.org/pypi/flask-query-builder)
This package allows you to filter and sort based on a request. Query parameter names follow the [JSON API specification](http://jsonapi.org/) as closely as possible.
## Prerequisites
If you are using flask-sqlalchemy you don't need to perform any setup steps.If however you are using vanilla sqlalchemy you need to assign your query object to your base model like so:
```python
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmakerengine = create_engine("sqlite:///db")
session = scoped_session(
sessionmaker(
autoflush=False,
autocommit=False,
bind=engine
)
)Model = declarative_base()
Model.query = session.query_property()
```## Basic usage
### Filter a query based on a request
`/users?filter[name]=John`
```python
from flask_query_builder.querying import QueryBuilderusers = (
QueryBuilder(User)
.allowed_filters(['name'])
.query
.all()
)# all `User`s that contain the string "John" in their name
```### Sorting a query based on a request
`/users?sort=id`
```python
users = (
QueryBuilder(User)
.allowed_sorts(['last_name'])
.query
.all()
)
# all `User`s sorted by ascending last_name
```### Works together nicely with existing queries
```python
query = User.query.filter_by(name="John")query = (
QueryBuilder(model=User, query=query) # start from an existing query
.allowed_filters(["first_name", "last_name"])
.query
)
```## Filtering
You specify the filters on the query by using the keyword `filter` followed by the name of the filter inside square brackets `?filter[name]=John`You can specify a list of values to filter by separated by commas `?filter[name]=John,Mary`
You can add multiple filters to the request `?filter[name]=John,Mary&filter[age]=30`
### Exact Filter
You can use an exact filter to perform an exact match on any fields that exist on the model by using `AllowedFilter.exact()`, for example:Request query: `/users?filter[first_name]=John`
```python
users = (
QueryBuilder(User)
.allowed_filters([
AllowedFilter.exact("first_name")
])
.query
.all()
)
```
If you use a string value instead of `AllowedFilter.exact` it has the same effect as it gets converted to an exact filter in the background for example:
```python
users = (
QueryBuilder(User)
.allowed_filters([
"first_name"
])
.query
.all()
)
```With the `exact` filter you can use a different filter name on the request to what is named on your model for example:
Request query: `/users?filter[name]=John`
```python
users = (
QueryBuilder(User)
.allowed_filters([
AllowedFilter.exact("name", "first_name")
])
.query
.all()
)
```
The first parameter is always the name used on the request and the second one is the internal name on your models.### Partial Filter
You can use a partial filter to perform a case-insensitive wildcard search on any fields that exist on the model by using `AllowedFilter.partial()`, for example:Request query: `/users?filter[first_name]=John`
```python
users = (
QueryBuilder(User)
.allowed_filters([
AllowedFilter.partial("first_name")
])
.query
.all()
)
```
With the `partial` filter you can use a different filter name on the request to what is named on your model for example:Request query: `/users?filter[name]=John`
```python
users = (
QueryBuilder(User)
.allowed_filters([
AllowedFilter.partial("name", "first_name")
])
.query
.all()
)
```
The first parameter is always the name used on the request and the second one is the internal name on your models.### Custom Filter
When your filter is more complex that a simple field on a model you can create a custom filter and add it using `AllowedFilter.custom()`To create a custom filter you need to inherit from `Filter` and implement the `filter` method defined in the base class.
Here we assume a user has a related model called Address where a user has an address.
We therefore create a custom filter where we can filter users by their address name.`Note: Remember to join any tables used in a filter like we do below.`
```python
from flask_query_builder.filters import Filter# query -> the query object that contains the existing queries
# model -> the model class that the QueryBuilder has been initialized on
# filter_name -> the external filter name used on the request
# values -> a list of values passed to the requestclass AddressRoadFilter(Filter):
def filter(self, query, model, filter_name, values):
if not len(values):
return query
return query.filter(Address.street_name.in_(values))users = (
QueryBuilder(User)
.allowed_filters([
AllowedFilter.custom("road", AddressRoadFilter())
])
.query
.join(User.address)
.all()
)
```## Sorting
You specify the sorts on the query by using the `sort` keySorting in ascending order is the default option `?sort=name`
Sorting in descending order is achieved by adding a `-` sign in front of the field `?sort=-name`
You can sort by multiple columns or custom sorts by separating values with a comma`?sort=name,id`
### Field Sort
You can use an field sort to sort by any fields that exist on the model by using `AllowedSort.field()`Request query: `/users?sort=first_name`:
```python
users = (
QueryBuilder(User)
.allowed_sorts([
AllowedSort.field("first_name")
])
.query
.all()
)
```
If you use a string value instead of `AllowedSort.field` it has the same effect as it gets converted to a field sort in the background for example:
```python
users = (
QueryBuilder(User)
.allowed_sorts([
"first_name"
])
.query
.all()
)
```
With the `field` sort you can use a different sort name on the request to what is named on your model for example:Request query: `/users?sort=name`:
```python
users = (
QueryBuilder(User)
.allowed_filters([
AllowedFilter.exact("name", "first_name")
])
.query
.all()
)
```
The first parameter is always the name used on the request and the second one is the internal name on your models.### Custom Sort
When your sort logic is more complex that a simple field on a model you can create a custom sort and add it using `AllowedSort.custom()`To create a custom sort you need to inherit from `Sort` and implement the `sort` method defined in the base class.
Here we assume a user has a related model called Address where a user has an address.
We therefore create a custom sort where we can sort users by their address name.`Note: Remember to join any tables used in a sort like we do below.`
```python
from flask_query_builder.sorts import Sort# query -> the query object that contains the existing queries
# model -> the model class that the QueryBuilder has been initialized on
# sort_name -> the external sort name used on the request
# descending -> specifies if the sort is in descending orderclass AddressRoadSort(Sort):
def sort(self, query, model, sort_name, descending):
if descending:
return query.order_by(Address.street_name.desc())
return query.order_by(Address.street_name)users = (
QueryBuilder(User)
.allowed_sorts([
AllowedSort.custom("road", AddressRoadSort())
])
.query
.join(User.address)
.all()
)
```## Exceptions
When using the `QueryBuilder` and adding any of the methods `allowed_sorts` or `allowed_filters`
if the frontend request a filter or a sort that was not included in any of those lists an Exception will be thrown letting
you know that the `filter` or the `sort` is not allowed.## Installation
You can install the package via pip:
```bash
pip install flask-query-builder
```## Documentation
You can find the documentation on https://petsas.dev/projects/flask-query-builder
Find yourself stuck using the package? Found a bug? Do you have general questions or suggestions for improving the media library? Feel free to [create an issue on GitHub](https://github.com/demoskp/flask-query-builder/issues), we'll try to address it as soon as possible.
If you've found a bug regarding security please mail [[email protected]](mailto:[email protected]) instead of using the issue tracker.
### Upgrading
Please see [UPGRADING.md](UPGRADING.md) for details.
### Testing
```bash
pytest tests/
```### Changelog
Please see [CHANGELOG](CHANGELOG.md) for more information what has changed recently.
## Contributing
Please see [CONTRIBUTING](https://github.com/demoskp/flask-query-builder/CONTRIBUTING.md) for details.
### Security
If you've found a bug regarding security please mail [[email protected]](mailto:[email protected]) instead of using the issue tracker.
## License
The MIT License (MIT). Please see [License File](LICENSE.md) for more information.