Metadata-Version: 2.4
Name: querymate
Version: 0.5.4
Summary: A structured query builder for FastAPI and SQLModel
Author-email: Mauricio Banduk <mauricio@banduk.com>
License-Expression: MIT
Project-URL: Homepage, https://github.com/banduk/querymate
Project-URL: Repository, https://github.com/banduk/querymate
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: fastapi>=0.109.0
Requires-Dist: sqlmodel>=0.0.14
Requires-Dist: pydantic>=2.6.0
Requires-Dist: pydantic-settings>=2.1.0
Requires-Dist: sqlalchemy>=2.0.0
Requires-Dist: greenlet>=3.0.0
Provides-Extra: dev
Requires-Dist: pytest>=8.0.0; extra == "dev"
Requires-Dist: pytest-cov>=4.1.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.26.0; extra == "dev"
Requires-Dist: ruff>=0.2.0; extra == "dev"
Requires-Dist: black>=24.1.0; extra == "dev"
Requires-Dist: isort>=5.13.0; extra == "dev"
Requires-Dist: mypy>=1.8.0; extra == "dev"
Requires-Dist: sphinx>=7.2.0; extra == "dev"
Requires-Dist: sphinx-rtd-theme>=2.0.0; extra == "dev"
Requires-Dist: sphinx-autodoc-typehints>=1.25.0; extra == "dev"
Requires-Dist: myst-parser>=2.0.0; extra == "dev"
Requires-Dist: sphinx-copybutton>=0.5.0; extra == "dev"
Requires-Dist: sphinx-design>=0.5.0; extra == "dev"
Requires-Dist: furo>=2024.0.0; extra == "dev"
Requires-Dist: httpx>=0.27.0; extra == "dev"
Requires-Dist: toml>=0.10.2; extra == "dev"
Requires-Dist: packaging>=24.0; extra == "dev"
Requires-Dist: build>=0.11.0; extra == "dev"
Requires-Dist: twine>=5.0.0; extra == "dev"
Requires-Dist: ipdb>=0.13.13; extra == "dev"
Requires-Dist: aiosqlite>=0.2.0; extra == "dev"
Dynamic: license-file

# 🔍 QueryMate

[![PyPI version](https://badge.fury.io/py/querymate.svg)](https://badge.fury.io/py/querymate)
[![codecov](https://codecov.io/gh/banduk/querymate/graph/badge.svg?token=CXN9YCLMMG)](https://codecov.io/gh/banduk/querymate)
[![Documentation](https://img.shields.io/badge/%F0%9F%93%98-documentation-blue?link=https%3A%2F%2Fbanduk.github.io%2Fquerymate%2F)](https://banduk.github.io/querymate/)
[![Python Version](https://img.shields.io/badge/python-3.11%2B-blue)](https://www.python.org/downloads/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

**A powerful query builder for FastAPI and SQLModel** — with full support for:

- ✅ Filtering
- ✅ Sorting
- ✅ Pagination (limit/offset)
- ✅ Field selection
- ✅ Query parameter parsing
- ✅ Async database support
- ✅ Built-in serialization

Built for teams that want to build robust APIs with FastAPI and SQLModel.

---

## ✨ Key Features

| Feature                       | Description                                                                 |
| ----------------------------- | --------------------------------------------------------------------------- |
| 🔍 Query Parameter Parsing     | Parse and validate query parameters with ease                               |
| 🎯 Filtering                  | Build complex filters with a simple interface                               |
| 📊 Sorting                    | Sort results by multiple fields                                            |
| 📄 Pagination                 | Limit and offset support for efficient data retrieval                      |
| 🎨 Field Selection            | Select specific fields to return                                           |
| 🏗️ Query Building             | Build SQL queries programmatically                                         |
| ⚡ Async Support              | Full support for async database operations                                 |
| 📦 Serialization              | Built-in serialization with support for relationships                      |

---

## 🚀 Quick Start

### Installation

```bash
pip install querymate
```

For async support, you'll also need to install the appropriate async database driver:

```bash
# For SQLite
pip install aiosqlite

# For PostgreSQL
pip install asyncpg

# For MySQL
pip install aiomysql
```

### Basic Usage

1. Define your SQLModel:

```python
from sqlmodel import SQLModel, Field, Relationship

class User(SQLModel, table=True):
    id: int = Field(primary_key=True)
    name: str
    email: str
    age: int
    posts: list["Post"] = Relationship(back_populates="author")

class Post(SQLModel, table=True):
    id: int = Field(primary_key=True)
    title: str
    content: str
    author_id: int = Field(foreign_key="user.id")
    author: User = Relationship(back_populates="posts")
```

2. Use QueryMate in your FastAPI route (Synchronous):

```python
from fastapi import FastAPI, Depends
from sqlmodel import Session
from querymate import QueryMate

app = FastAPI()

@app.get("/users")
def get_users(
    query: QueryMate = Depends(QueryMate.fastapi_dependency),
    db: Session = Depends(get_db)
):
    # Returns serialized results (dictionaries)
    return query.run(db, User)

@app.get("/users/raw")
def get_users_raw(
    query: QueryMate = Depends(QueryMate.fastapi_dependency),
    db: Session = Depends(get_db)
):
    # Returns raw model instances
    return query.run_raw(db, User)
```

3. Use QueryMate with Async Database (Asynchronous):

```python
from fastapi import FastAPI, Depends
from sqlmodel.ext.asyncio.session import AsyncSession
from sqlalchemy.ext.asyncio import create_async_engine
from sqlalchemy.orm import sessionmaker
from querymate import QueryMate

app = FastAPI()

# Create async database engine
engine = create_async_engine("sqlite+aiosqlite:///example.db")

# Create async session factory
async_session = sessionmaker(
    engine,
    class_=AsyncSession,
    expire_on_commit=False,
    autocommit=False,
    autoflush=False,
)

# Database dependency
async def get_db() -> AsyncGenerator[AsyncSession, None]:
    async with async_session() as session:
        try:
            yield session
        finally:
            await session.close()

@app.get("/users")
async def get_users(
    query: QueryMate = Depends(QueryMate.fastapi_dependency),
    db: AsyncSession = Depends(get_db)
):
    # Returns serialized results (dictionaries)
    return await query.run_async(db, User)

@app.get("/users/raw")
async def get_users_raw(
    query: QueryMate = Depends(QueryMate.fastapi_dependency),
    db: AsyncSession = Depends(get_db)
):
    # Returns raw model instances
    return await query.run_raw_async(db, User)
```

### Advanced Usage

```python
# Example query parameters
# ?q={"filter": {"age": {"gt": 18}}, "sort": ["-name", "age"], "limit": 10, "offset": 0, "select": ["id", "name", {"posts": ["title"]}]}

@app.get("/users")
async def get_users(
    query: QueryMate = Depends(QueryMate.fastapi_dependency),
    db: AsyncSession = Depends(get_db)
):
    # The query will be built and executed automatically
    # Results will be serialized according to the fields
    return await query.run_async(db, User)
```

### Pagination Metadata Response

In addition to plain lists, you can include pagination metadata alongside items.
Enable it via the query flag or force it via the method parameter:

```python
# Force via method
result = query.run(db, User, force_pagination=True)
# Or async:
# result = await query.run_async(db, User, force_pagination=True)

# Respect the query flag / instance setting
result2 = Querymate(include_pagination=True).run(db, User)

# Response shape
# {
#   "items": [{"id": 1, "name": "John"}, ...],
#   "pagination": {
#     "total": 57,
#     "page": 2,
#     "size": 10,
#     "pages": 6,
#     "previous_page": 1,
#     "next_page": 3
#   }
# }
```

Query flag name and default behavior are configurable (see settings):

- `PAGINATION_PARAM_NAME` (default: `include_pagination`)
- `DEFAULT_RETURN_PAGINATION` (default: `False`)

### Serialization

QueryMate includes built-in serialization capabilities that transform query results into dictionaries containing only the requested fields. This helps reduce payload size and improve performance.

Features:
- Direct field selection
- Nested relationships
- Both list and non-list relationships
- Automatic handling of null values

Example:
```python
# Returns serialized results with only the requested fields
results = query.run(db, User)
# [
#     {
#         "id": 1,
#         "name": "John",
#         "posts": [
#             {"id": 1, "title": "Post 1"},
#             {"id": 2, "title": "Post 2"}
#         ]
#     }
# ]

# Returns raw model instances
raw_results = query.run_raw(db, User)
# [User(id=1, name="John", posts=[Post(id=1, title="Post 1"), Post(id=2, title="Post 2")])]
```

---

## 🛠️ Development Guide

### Project Setup

```bash
# Clone the repository
git clone https://github.com/yourusername/querymate.git
cd querymate

# Set up the development environment
make setup

# Activate the virtual environment
source .venv/bin/activate
```

### Project Structure

```
querymate/
├── core/                         # Core functionality
│   ├── querymate.py              # Main QueryMate class
│   ├── filter.py                 # Filter handling
│   ├── query_builder.py          # Query building
│   └── __init__.py              # Package initialization
├── tests/                        # Test suite
├── docs/                         # Documentation
│   ├── source/                  # Sphinx documentation source
│   │   ├── api/                 # API documentation
│   │   ├── examples/            # Usage examples
│   │   └── usage/               # Usage guides
│   └── conf.py                  # Sphinx configuration
└── examples/                     # Example usage
```

### Development Workflow

1. Create a new feature branch:
   ```bash
   git checkout -b feature/your-feature-name
   ```

2. Make your changes and run tests:
   ```bash
   make test
   ```

3. Run code quality checks:
   ```bash
   make lint
   make format
   python -m mypy .
   ```

4. Update documentation:
   ```bash
   make docs
   ```

5. Submit a pull request

### Testing

```bash
# Run all tests
make test

# Run tests with coverage
python -m pytest --cov=querymate
```

### Documentation

```bash
# Build the documentation
make docs

# View the documentation
open docs/_build/html/index.html
```

---

## 📚 Documentation

For detailed documentation, visit [banduk.github.io/querymate](https://banduk.github.io/querymate).

---

## 🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

---

## 📄 License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
