Add search feature for todos
- Add /api/todos/search endpoint with text search capability - Implement filtering by completed status - Include pagination and result count - Update documentation with new endpoint details generated with BackendIM... (backend.im)
This commit is contained in:
parent
262bc6c71d
commit
76dc7f1991
18
README.md
18
README.md
@ -5,6 +5,7 @@ This is a FastAPI application that implements a simple Todo API.
|
|||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Create, read, update, and delete todo items
|
- Create, read, update, and delete todo items
|
||||||
|
- Search todos by text query with optional filtering
|
||||||
- Health check endpoint
|
- Health check endpoint
|
||||||
- SQLite database for data storage
|
- SQLite database for data storage
|
||||||
- FastAPI automatic documentation
|
- FastAPI automatic documentation
|
||||||
@ -41,8 +42,23 @@ The server will start at http://localhost:8000
|
|||||||
## API Endpoints
|
## API Endpoints
|
||||||
|
|
||||||
- `GET /api/todos`: List all todos
|
- `GET /api/todos`: List all todos
|
||||||
|
- `GET /api/todos/search`: Search todos by text query
|
||||||
- `POST /api/todos`: Create a new todo
|
- `POST /api/todos`: Create a new todo
|
||||||
- `GET /api/todos/{todo_id}`: Get a specific todo
|
- `GET /api/todos/{todo_id}`: Get a specific todo
|
||||||
- `PUT /api/todos/{todo_id}`: Update a todo
|
- `PUT /api/todos/{todo_id}`: Update a todo
|
||||||
- `DELETE /api/todos/{todo_id}`: Delete a todo
|
- `DELETE /api/todos/{todo_id}`: Delete a todo
|
||||||
- `GET /health`: Check application health
|
- `GET /health`: Check application health
|
||||||
|
|
||||||
|
### Search Endpoint
|
||||||
|
|
||||||
|
The search endpoint allows searching todos by text query with additional filters:
|
||||||
|
|
||||||
|
```
|
||||||
|
GET /api/todos/search?q=search_term&completed=true&skip=0&limit=10
|
||||||
|
```
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
- `q`: (required) Search text to match in title or description
|
||||||
|
- `completed`: (optional) Filter by completion status (true/false)
|
||||||
|
- `skip`: (optional) Number of records to skip for pagination
|
||||||
|
- `limit`: (optional) Maximum number of records to return
|
@ -1,10 +1,11 @@
|
|||||||
from fastapi import APIRouter, Depends, HTTPException, status
|
from fastapi import APIRouter, Depends, HTTPException, status, Query
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from typing import List
|
from typing import List, Optional
|
||||||
|
from sqlalchemy import or_
|
||||||
|
|
||||||
from app.database import get_db
|
from app.database import get_db
|
||||||
from app.models.todo import Todo as TodoModel
|
from app.models.todo import Todo as TodoModel
|
||||||
from app.schemas.todo import Todo, TodoCreate, TodoUpdate
|
from app.schemas.todo import Todo, TodoCreate, TodoUpdate, TodoSearchResults
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
@ -13,6 +14,42 @@ def get_todos(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
|
|||||||
todos = db.query(TodoModel).offset(skip).limit(limit).all()
|
todos = db.query(TodoModel).offset(skip).limit(limit).all()
|
||||||
return todos
|
return todos
|
||||||
|
|
||||||
|
@router.get("/search", response_model=TodoSearchResults)
|
||||||
|
def search_todos(
|
||||||
|
q: str = Query(..., description="Search query string"),
|
||||||
|
completed: Optional[bool] = Query(None, description="Filter by completion status"),
|
||||||
|
skip: int = 0,
|
||||||
|
limit: int = 100,
|
||||||
|
db: Session = Depends(get_db)
|
||||||
|
):
|
||||||
|
query = db.query(TodoModel)
|
||||||
|
|
||||||
|
# Apply search filter (search in title and description)
|
||||||
|
query = query.filter(
|
||||||
|
or_(
|
||||||
|
TodoModel.title.contains(q),
|
||||||
|
TodoModel.description.contains(q)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Apply completion status filter if provided
|
||||||
|
if completed is not None:
|
||||||
|
query = query.filter(TodoModel.completed == completed)
|
||||||
|
|
||||||
|
# Count total results
|
||||||
|
total = query.count()
|
||||||
|
|
||||||
|
# Apply pagination
|
||||||
|
todos = query.offset(skip).limit(limit).all()
|
||||||
|
|
||||||
|
return {
|
||||||
|
"todos": todos,
|
||||||
|
"total": total,
|
||||||
|
"skip": skip,
|
||||||
|
"limit": limit,
|
||||||
|
"query": q
|
||||||
|
}
|
||||||
|
|
||||||
@router.post("/", response_model=Todo, status_code=status.HTTP_201_CREATED)
|
@router.post("/", response_model=Todo, status_code=status.HTTP_201_CREATED)
|
||||||
def create_todo(todo: TodoCreate, db: Session = Depends(get_db)):
|
def create_todo(todo: TodoCreate, db: Session = Depends(get_db)):
|
||||||
db_todo = TodoModel(**todo.dict())
|
db_todo = TodoModel(**todo.dict())
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Optional
|
from typing import Optional, List
|
||||||
|
|
||||||
class TodoBase(BaseModel):
|
class TodoBase(BaseModel):
|
||||||
title: str
|
title: str
|
||||||
@ -21,4 +21,11 @@ class Todo(TodoBase):
|
|||||||
updated_at: Optional[datetime] = None
|
updated_at: Optional[datetime] = None
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
orm_mode = True
|
orm_mode = True
|
||||||
|
|
||||||
|
class TodoSearchResults(BaseModel):
|
||||||
|
todos: List[Todo]
|
||||||
|
total: int
|
||||||
|
skip: int
|
||||||
|
limit: int
|
||||||
|
query: str
|
Loading…
x
Reference in New Issue
Block a user