|
|
|
@@ -31,13 +31,9 @@ GET /articles?page=2&items_per_page=20
|
|
|
|
```json
|
|
|
|
```json
|
|
|
|
{
|
|
|
|
{
|
|
|
|
"items": [...],
|
|
|
|
"items": [...],
|
|
|
|
"pagination": {
|
|
|
|
"total": 143,
|
|
|
|
"total_count": 143,
|
|
|
|
|
|
|
|
"items_per_page": 20,
|
|
|
|
|
|
|
|
"page": 2,
|
|
|
|
"page": 2,
|
|
|
|
"has_more": true,
|
|
|
|
"total_pages": 8
|
|
|
|
"pages": 8
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
@@ -56,12 +52,8 @@ GET /articles?cursor=eyJpZCI6IjEyMyJ9&items_per_page=20
|
|
|
|
```json
|
|
|
|
```json
|
|
|
|
{
|
|
|
|
{
|
|
|
|
"items": [...],
|
|
|
|
"items": [...],
|
|
|
|
"pagination": {
|
|
|
|
"next_cursor": "eyJpZCI6IjE0MyJ9",
|
|
|
|
"next_cursor": "eyJjcmVhdGVkX2F0IjogIjIwMjYtMDMtMTBUMDg6MTQ6MDBaIn0=",
|
|
|
|
"has_next": true
|
|
|
|
"prev_cursor": null,
|
|
|
|
|
|
|
|
"items_per_page": 20,
|
|
|
|
|
|
|
|
"has_more": true
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
@@ -215,19 +207,22 @@ With the CRUD factory declared, routes become thin wrappers. Each route uses [Ar
|
|
|
|
async def list_articles_offset(
|
|
|
|
async def list_articles_offset(
|
|
|
|
session: SessionDep,
|
|
|
|
session: SessionDep,
|
|
|
|
params: Annotated[
|
|
|
|
params: Annotated[
|
|
|
|
dict,
|
|
|
|
dict[str, Any],
|
|
|
|
Depends(
|
|
|
|
Depends(ArticleCrud.offset_params(default_page_size=20, max_page_size=100)),
|
|
|
|
ArticleCrud.offset_paginate_params(
|
|
|
|
|
|
|
|
default_page_size=20,
|
|
|
|
|
|
|
|
max_page_size=100,
|
|
|
|
|
|
|
|
default_order_field=Article.created_at,
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
],
|
|
|
|
],
|
|
|
|
|
|
|
|
filter_by: Annotated[dict[str, list[str]], Depends(ArticleCrud.filter_params())],
|
|
|
|
|
|
|
|
order_by: Annotated[
|
|
|
|
|
|
|
|
OrderByClause | None,
|
|
|
|
|
|
|
|
Depends(ArticleCrud.order_params(default_field=Article.created_at)),
|
|
|
|
|
|
|
|
],
|
|
|
|
|
|
|
|
search: str | None = None,
|
|
|
|
) -> OffsetPaginatedResponse[ArticleRead]:
|
|
|
|
) -> OffsetPaginatedResponse[ArticleRead]:
|
|
|
|
return await ArticleCrud.offset_paginate(
|
|
|
|
return await ArticleCrud.offset_paginate(
|
|
|
|
session=session,
|
|
|
|
session=session,
|
|
|
|
**params,
|
|
|
|
**params,
|
|
|
|
|
|
|
|
search=search,
|
|
|
|
|
|
|
|
filter_by=filter_by or None,
|
|
|
|
|
|
|
|
order_by=order_by,
|
|
|
|
schema=ArticleRead,
|
|
|
|
schema=ArticleRead,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
```
|
|
|
|
```
|
|
|
|
@@ -236,7 +231,7 @@ async def list_articles_offset(
|
|
|
|
|
|
|
|
|
|
|
|
**Example request:**
|
|
|
|
**Example request:**
|
|
|
|
```
|
|
|
|
```
|
|
|
|
GET /articles/offset?page=2&items_per_page=2&search=fastapi&status=published&order_by=created_at&order_dir=desc
|
|
|
|
GET /articles/offset?page=2&items_per_page=2&search=fastapi&filter_by[status]=published&order_by=created_at&order_dir=desc
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**Example response:**
|
|
|
|
**Example response:**
|
|
|
|
@@ -264,16 +259,13 @@ GET /articles/offset?page=2&items_per_page=2&search=fastapi&status=published&ord
|
|
|
|
"total_count": 47,
|
|
|
|
"total_count": 47,
|
|
|
|
"items_per_page": 2,
|
|
|
|
"items_per_page": 2,
|
|
|
|
"page": 2,
|
|
|
|
"page": 2,
|
|
|
|
"has_more": true,
|
|
|
|
"has_more": true
|
|
|
|
"pages": 24
|
|
|
|
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"pagination_type": "offset",
|
|
|
|
"pagination_type": "offset",
|
|
|
|
"filter_attributes": {
|
|
|
|
"filter_attributes": {
|
|
|
|
"status": ["draft", "published", "archived"],
|
|
|
|
"status": ["draft", "published", "archived"],
|
|
|
|
"category__name": ["Python", "DevOps", "Architecture"]
|
|
|
|
"category__name": ["Python", "DevOps", "Architecture"]
|
|
|
|
},
|
|
|
|
}
|
|
|
|
"search_columns": ["title", "body", "category__name"],
|
|
|
|
|
|
|
|
"order_columns": ["title", "created_at"]
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
@@ -284,19 +276,22 @@ GET /articles/offset?page=2&items_per_page=2&search=fastapi&status=published&ord
|
|
|
|
async def list_articles_cursor(
|
|
|
|
async def list_articles_cursor(
|
|
|
|
session: SessionDep,
|
|
|
|
session: SessionDep,
|
|
|
|
params: Annotated[
|
|
|
|
params: Annotated[
|
|
|
|
dict,
|
|
|
|
dict[str, Any],
|
|
|
|
Depends(
|
|
|
|
Depends(ArticleCrud.cursor_params(default_page_size=20, max_page_size=100)),
|
|
|
|
ArticleCrud.cursor_paginate_params(
|
|
|
|
|
|
|
|
default_page_size=20,
|
|
|
|
|
|
|
|
max_page_size=100,
|
|
|
|
|
|
|
|
default_order_field=Article.created_at,
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
],
|
|
|
|
],
|
|
|
|
|
|
|
|
filter_by: Annotated[dict[str, list[str]], Depends(ArticleCrud.filter_params())],
|
|
|
|
|
|
|
|
order_by: Annotated[
|
|
|
|
|
|
|
|
OrderByClause | None,
|
|
|
|
|
|
|
|
Depends(ArticleCrud.order_params(default_field=Article.created_at)),
|
|
|
|
|
|
|
|
],
|
|
|
|
|
|
|
|
search: str | None = None,
|
|
|
|
) -> CursorPaginatedResponse[ArticleRead]:
|
|
|
|
) -> CursorPaginatedResponse[ArticleRead]:
|
|
|
|
return await ArticleCrud.cursor_paginate(
|
|
|
|
return await ArticleCrud.cursor_paginate(
|
|
|
|
session=session,
|
|
|
|
session=session,
|
|
|
|
**params,
|
|
|
|
**params,
|
|
|
|
|
|
|
|
search=search,
|
|
|
|
|
|
|
|
filter_by=filter_by or None,
|
|
|
|
|
|
|
|
order_by=order_by,
|
|
|
|
schema=ArticleRead,
|
|
|
|
schema=ArticleRead,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
```
|
|
|
|
```
|
|
|
|
@@ -305,7 +300,7 @@ async def list_articles_cursor(
|
|
|
|
|
|
|
|
|
|
|
|
**Example request (first page):**
|
|
|
|
**Example request (first page):**
|
|
|
|
```
|
|
|
|
```
|
|
|
|
GET /articles/cursor?items_per_page=2&search=fastapi&status=published
|
|
|
|
GET /articles/cursor?items_per_page=2&search=fastapi&filter_by[status]=published
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**Example response:**
|
|
|
|
**Example response:**
|
|
|
|
@@ -339,9 +334,7 @@ GET /articles/cursor?items_per_page=2&search=fastapi&status=published
|
|
|
|
"filter_attributes": {
|
|
|
|
"filter_attributes": {
|
|
|
|
"status": ["draft", "published", "archived"],
|
|
|
|
"status": ["draft", "published", "archived"],
|
|
|
|
"category__name": ["Python", "DevOps", "Architecture"]
|
|
|
|
"category__name": ["Python", "DevOps", "Architecture"]
|
|
|
|
},
|
|
|
|
}
|
|
|
|
"search_columns": ["title", "body", "category__name"],
|
|
|
|
|
|
|
|
"order_columns": ["title", "created_at"]
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
@@ -359,19 +352,22 @@ You can also expose a single endpoint that supports both strategies via a `pagin
|
|
|
|
async def list_articles(
|
|
|
|
async def list_articles(
|
|
|
|
session: SessionDep,
|
|
|
|
session: SessionDep,
|
|
|
|
params: Annotated[
|
|
|
|
params: Annotated[
|
|
|
|
dict,
|
|
|
|
dict[str, Any],
|
|
|
|
Depends(
|
|
|
|
Depends(ArticleCrud.paginate_params(default_page_size=20, max_page_size=100)),
|
|
|
|
ArticleCrud.paginate_params(
|
|
|
|
|
|
|
|
default_page_size=20,
|
|
|
|
|
|
|
|
max_page_size=100,
|
|
|
|
|
|
|
|
default_order_field=Article.created_at,
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
],
|
|
|
|
],
|
|
|
|
|
|
|
|
filter_by: Annotated[dict[str, list[str]], Depends(ArticleCrud.filter_params())],
|
|
|
|
|
|
|
|
order_by: Annotated[
|
|
|
|
|
|
|
|
OrderByClause | None,
|
|
|
|
|
|
|
|
Depends(ArticleCrud.order_params(default_field=Article.created_at)),
|
|
|
|
|
|
|
|
],
|
|
|
|
|
|
|
|
search: str | None = None,
|
|
|
|
) -> PaginatedResponse[ArticleRead]:
|
|
|
|
) -> PaginatedResponse[ArticleRead]:
|
|
|
|
return await ArticleCrud.paginate(
|
|
|
|
return await ArticleCrud.paginate(
|
|
|
|
session,
|
|
|
|
session,
|
|
|
|
**params,
|
|
|
|
**params,
|
|
|
|
|
|
|
|
search=search,
|
|
|
|
|
|
|
|
filter_by=filter_by or None,
|
|
|
|
|
|
|
|
order_by=order_by,
|
|
|
|
schema=ArticleRead,
|
|
|
|
schema=ArticleRead,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
```
|
|
|
|
```
|
|
|
|
@@ -380,7 +376,7 @@ async def list_articles(
|
|
|
|
|
|
|
|
|
|
|
|
The response shape adapts to the chosen strategy. With `pagination_type=offset` (default):
|
|
|
|
The response shape adapts to the chosen strategy. With `pagination_type=offset` (default):
|
|
|
|
```
|
|
|
|
```
|
|
|
|
GET /articles/?pagination_type=offset&page=1&items_per_page=2&status=published
|
|
|
|
GET /articles/?pagination_type=offset&page=1&items_per_page=2&filter_by[status]=published
|
|
|
|
```
|
|
|
|
```
|
|
|
|
```json
|
|
|
|
```json
|
|
|
|
{
|
|
|
|
{
|
|
|
|
@@ -390,21 +386,18 @@ GET /articles/?pagination_type=offset&page=1&items_per_page=2&status=published
|
|
|
|
"items_per_page": 2,
|
|
|
|
"items_per_page": 2,
|
|
|
|
"page": 1,
|
|
|
|
"page": 1,
|
|
|
|
"has_more": true
|
|
|
|
"has_more": true
|
|
|
|
"pages": 24,
|
|
|
|
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"pagination_type": "offset",
|
|
|
|
"pagination_type": "offset",
|
|
|
|
"filter_attributes": {
|
|
|
|
"filter_attributes": {
|
|
|
|
"status": ["draft", "published", "archived"],
|
|
|
|
"status": ["draft", "published", "archived"],
|
|
|
|
"category__name": ["Python", "DevOps", "Architecture"]
|
|
|
|
"category__name": ["Python", "DevOps", "Architecture"]
|
|
|
|
},
|
|
|
|
}
|
|
|
|
"search_columns": ["title", "body", "category__name"],
|
|
|
|
|
|
|
|
"order_columns": ["title", "created_at"]
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
With `pagination_type=cursor`:
|
|
|
|
With `pagination_type=cursor`:
|
|
|
|
```
|
|
|
|
```
|
|
|
|
GET /articles/?pagination_type=cursor&items_per_page=2&status=published
|
|
|
|
GET /articles/?pagination_type=cursor&items_per_page=2&filter_by[status]=published
|
|
|
|
```
|
|
|
|
```
|
|
|
|
```json
|
|
|
|
```json
|
|
|
|
{
|
|
|
|
{
|
|
|
|
@@ -419,9 +412,7 @@ GET /articles/?pagination_type=cursor&items_per_page=2&status=published
|
|
|
|
"filter_attributes": {
|
|
|
|
"filter_attributes": {
|
|
|
|
"status": ["draft", "published", "archived"],
|
|
|
|
"status": ["draft", "published", "archived"],
|
|
|
|
"category__name": ["Python", "DevOps", "Architecture"]
|
|
|
|
"category__name": ["Python", "DevOps", "Architecture"]
|
|
|
|
},
|
|
|
|
}
|
|
|
|
"search_columns": ["title", "body", "category__name"],
|
|
|
|
|
|
|
|
"order_columns": ["title", "created_at"]
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|