Skip to content

Commit bd9ddad

Browse files
authored
Merge pull request #9 from mts-ai/feature/includes-and-other-huge-improvements
Includes and generics
2 parents 3d8eeb5 + 6fc89b3 commit bd9ddad

File tree

117 files changed

+6283
-839
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

117 files changed

+6283
-839
lines changed

.coveragerc

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# .coveragerc to control coverage.py
2+
[run]
3+
branch = True
4+
omit =
5+
tests/*
6+
examples/*
7+
docs/*
8+
# omit anything in a .local directory anywhere
9+
# */.local/*
10+
# omit everything in /usr
11+
# /usr/*
12+
# omit this single file
13+
# utils/tirefire.py
14+
15+
[report]
16+
# Regexes for lines to exclude from consideration
17+
exclude_also =
18+
# Don't complain about missing debug-only code:
19+
def __repr__
20+
if self\.debug
21+
22+
# Don't complain if tests don't hit defensive assertion code:
23+
raise AssertionError
24+
raise NotImplementedError
25+
26+
# Don't complain if non-runnable code isn't run:
27+
if 0:
28+
if __name__ == .__main__.:
29+
30+
# Don't complain about abstract methods, they aren't run:
31+
@(abc\.)?abstractmethod
32+
33+
ignore_errors = True
34+
35+
[html]
36+
directory = coverage_html_report

.github/workflows/testing.yml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
name: Python testing
2+
3+
on:
4+
- push
5+
6+
jobs:
7+
check:
8+
runs-on: ubuntu-latest
9+
strategy:
10+
matrix:
11+
python-version:
12+
- "3.9"
13+
- "3.10"
14+
- "3.11"
15+
16+
steps:
17+
- uses: actions/checkout@v3
18+
- name: Set up Python ${{ matrix.python-version }}
19+
uses: actions/setup-python@v4
20+
with:
21+
python-version: ${{ matrix.python-version }}
22+
- name: Install poetry
23+
run: |
24+
python -m pip install --upgrade pip poetry pre-commit
25+
poetry config virtualenvs.create false --local
26+
- name: Install dependencies
27+
run: poetry install --all-extras
28+
- name: Lint code
29+
run: pre-commit run --all-files
30+
- name: Test with pytest
31+
run: pytest -s -vv tests/

.pre-commit-config.yaml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
repos:
2+
- repo: https://github.com/pre-commit/pre-commit-hooks
3+
rev: "v3.2.0"
4+
hooks:
5+
- id: trailing-whitespace
6+
- id: end-of-file-fixer
7+
- id: check-yaml
8+
- id: check-added-large-files
9+
- id: mixed-line-ending
10+
- id: requirements-txt-fixer
11+
12+
- repo: https://github.com/psf/black
13+
rev: "23.3.0"
14+
hooks:
15+
- id: black
16+
17+
- repo: https://github.com/charliermarsh/ruff-pre-commit
18+
rev: "v0.0.269"
19+
hooks:
20+
- id: ruff
21+
args: [--fix, --exit-non-zero-on-fix]

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1818
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1919
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2020
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21-
THE SOFTWARE.
21+
THE SOFTWARE.

MANIFEST.in

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
include README.md
22
include LICENSE
3-
include requirements.txt
3+
include requirements.txt

README.md

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
[![Documentation Status](https://readthedocs.org/projects/fastapi-jsonapi/badge/?version=latest)](https://fastapi-jsonapi.readthedocs.io/en/latest/?badge=latest)
2+
![PyPI](https://img.shields.io/pypi/v/fastapi-jsonapi?label=PyPI)
3+
14
# FastAPI-JSONAPI
25

36
FastAPI-JSONAPI is a FastAPI extension for building REST APIs.
@@ -36,7 +39,6 @@ from sqlalchemy.sql import Select
3639
from fastapi_jsonapi import RoutersJSONAPI
3740
from fastapi_jsonapi import SqlalchemyEngine
3841
from fastapi_jsonapi.data_layers.orm import DBORMType
39-
from fastapi_jsonapi.openapi import custom_openapi
4042
from fastapi_jsonapi.querystring import QueryStringManager
4143
from fastapi_jsonapi.schema import JSONAPIResultListSchema
4244
from fastapi_jsonapi.schema import collect_app_orm_schemas
@@ -115,7 +117,12 @@ class UserDetail:
115117
return UserSchema.from_orm(user)
116118

117119
@classmethod
118-
async def patch(cls, obj_id: int, data: UserPatchSchema, session: AsyncSession = Depends(Connector.get_session)) -> UserSchema:
120+
async def patch(
121+
cls,
122+
obj_id: int,
123+
data: UserPatchSchema,
124+
session: AsyncSession = Depends(Connector.get_session),
125+
) -> UserSchema:
119126
user: User = (await session.execute(select(User).where(User.id == obj_id))).scalar_one()
120127
user.first_name = data.first_name
121128
await session.commit()
@@ -131,7 +138,9 @@ class UserDetail:
131138
class UserList:
132139
@classmethod
133140
async def get(
134-
cls, query_params: QueryStringManager, session: AsyncSession = Depends(Connector.get_session)
141+
cls,
142+
query_params: QueryStringManager,
143+
session: AsyncSession = Depends(Connector.get_session),
135144
) -> Union[Select, JSONAPIResultListSchema]:
136145
user_query = select(User)
137146
dl = SqlalchemyEngine(query=user_query, schema=UserSchema, model=User, session=session)
@@ -161,7 +170,7 @@ def add_routes(app: FastAPI) -> List[Dict[str, Any]]:
161170

162171
routers: APIRouter = APIRouter()
163172
RoutersJSONAPI(
164-
routers=routers,
173+
router=routers,
165174
path="/user",
166175
tags=["User"],
167176
class_detail=UserDetail,
@@ -200,14 +209,12 @@ def create_app() -> FastAPI:
200209
)
201210
add_routes(app)
202211
app.on_event("startup")(sqlalchemy_init)
203-
custom_openapi(app, title="API for SQLAlchemy")
204212
collect_app_orm_schemas(app)
205213
return app
206214

207215

208216
app = create_app()
209217

210-
211218
if __name__ == "__main__":
212219
uvicorn.run(
213220
"test:app",

docs/api_filtering_example.rst

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,4 +86,3 @@ Response:
8686

8787
.. literalinclude:: ./http_snippets/snippets/api_filtering__get_persons__filter_word_contains_in_array_result
8888
:language: HTTP
89-

docs/changelog.rst

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,34 @@
11
Changelog
2+
#########
3+
4+
5+
**1.0.0**
26
*********
37

8+
Backward-incompatible changes, improvements, bug fixes
9+
======================================================
10+
11+
* Includes (see :ref:`example with many-to-many <include_many_to_many>`) - any level of includes is now supported (tested with 4);
12+
* View Classes generics (Detail View and List View);
13+
* View Classes now use instance-level methods (breaking change, previously ``classmethods`` were used);
14+
* Pydantic schemas now have to be inherited from custom BaseModel methods (breaking change, previously all schemas were supported). It uses custom `registry class <https://github.com/mts-ai/FastAPI-JSONAPI/blob/188093e967bb80b7a1f0a86e754a52e47f252044/fastapi_jsonapi/schema_base.py#L33>`_, so we can collect and resolve all schemas. Maybe there's some workaround to collect all known schemas;
15+
* Improved interactive docs, request and response examples now have more info, more schemas appear in docs;
16+
* Reworked schemas resolving and building;
17+
* Fixed filtering (schemas resolving fix);
18+
* Create custom sql filters :ref:`example <custom_sql_filtering>`;
19+
* Add linters: black, ruff;
20+
* Add pre-commit;
21+
* Add autotests with pytest;
22+
* Add poetry, configure dependencies groups;
23+
* Add GitHub Action with linting and testing;
24+
* Upgrade examples;
25+
* Update docs.
26+
27+
`@mahenzon`_
28+
29+
430
**0.2.1**
5-
=========
31+
*********
632

733
Enhancements and bug fixes
834
==========================
@@ -11,7 +37,7 @@ Enhancements and bug fixes
1137

1238

1339
**0.2.0**
14-
=========
40+
*********
1541

1642
Enhancements and bug fixes
1743
==========================
@@ -21,3 +47,4 @@ Enhancements and bug fixes
2147

2248

2349
.. _`@znbiz`: https://github.com/znbiz
50+
.. _`@mahenzon`: https://github.com/mahenzon

docs/conf.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
extensions = [
3434
"sphinx.ext.autodoc",
3535
"sphinx.ext.autosummary",
36+
"sphinx.ext.autosectionlabel",
3637
]
3738

3839
# Add any paths that contain templates here, relative to this directory.
@@ -137,6 +138,8 @@
137138
"page_width": "1080px",
138139
"fixed_sidebar": True,
139140
"code_font_size": "0.8em",
141+
"show_nav_level": 2,
142+
"navigation_depth": 2,
140143
}
141144

142145
# Add any paths that contain custom themes here, relative to this directory.

docs/custom_sql_filtering.rst

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
.. _custom_sql_filtering:
2+
3+
4+
Custom SQL filtering
5+
####################
6+
7+
.. currentmodule:: fastapi_jsonapi
8+
9+
Sometimes you need custom filtering that's not supported natively.
10+
You can define new filtering rules as in this example:
11+
12+
13+
14+
Prepare pydantic schema which is used in RoutersJSONAPI as schema
15+
-----------------------------------------------------------------
16+
17+
18+
``schemas/picture.py``:
19+
20+
.. literalinclude:: ../examples/custom_filter_example.py
21+
:language: python
22+
23+
24+
Declare models as usual, create routes as usual.
25+
26+
Search for objects
27+
------------------
28+
29+
30+
.. note::
31+
Note that url has to be quoted. It's unquoted only for an example
32+
33+
Request:
34+
35+
36+
.. sourcecode:: http
37+
38+
GET /pictures?filter=[{"name":"picture.meta","op":"jsonb_contains","val":{"location":"Moscow"}}] HTTP/1.1
39+
Accept: application/vnd.api+json
40+
41+
42+
Filter value has to be a valid JSON:
43+
44+
.. sourcecode:: JSON
45+
46+
[
47+
{
48+
"name":"picture.meta",
49+
"op":"jsonb_contains",
50+
"val":{
51+
"location":"Moscow"
52+
}
53+
}
54+
]

0 commit comments

Comments
 (0)