# Django-Flex

<p align="center">
    <em>A flexible query language for Django — let your frontend dynamically construct database queries</em>
</p>

<p align="center">
    <a href="https://pypi.org/project/django-flex/">
        <img src="https://img.shields.io/pypi/v/django-flex.svg" alt="PyPI version">
    </a>
    <a href="https://pypi.org/project/django-flex/">
        <img src="https://img.shields.io/pypi/pyversions/django-flex.svg" alt="Python versions">
    </a>
    <a href="https://github.com/your-org/django-flex/blob/main/LICENSE">
        <img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License">
    </a>
</p>

---

**Your first API in 5 minutes. No serializers. No viewsets. Just config.**

## Features

- **Field Selection** — Request only the fields you need, including nested relations
- **JSONField Support** — Seamless dot notation for nested JSON data
- **Dynamic Filtering** — Full Django ORM operator support with composable AND/OR/NOT
- **Smart Pagination** — Limit/offset with cursor-based continuation
- **Built-in Security** — Row-level, field-level, and operation-level permissions
- **Automatic Optimization** — N+1 prevention with smart `select_related`
- **Django-Native** — Feels like a natural extension of Django

## Installation

```bash
pip install django-flex
```

## Quick Start

### 1. Add to Django

```python
# settings.py
INSTALLED_APPS = ['django_flex', ...]

MIDDLEWARE = ['django_flex.middleware.FlexMiddleware', ...]

DJANGO_FLEX = {
    'EXPOSE': {
        'booking': {
            'staff': {
                'fields': ['id', 'status', 'customer.name', 'scheduled_date'],
                'ops': ['get', 'list', 'add', 'edit', 'delete'],
            },
        },
    },
}
```

### 2. Add URL

```python
# urls.py
urlpatterns = [
    path('api/', include('django_flex.urls')),
]
```

**Done.** Your API is live at `/api/bookings/`.

---

## CRUD Operations

### List

```javascript
fetch('/api/bookings/')
```
```json
{
    "results": {
        "1": {
            "id": 1,
            "status": "confirmed"
        },
        "2": {
            "id": 2,
            "status": "pending"
        }
    }
}
```

### Get

```javascript
fetch('/api/bookings/1')
```
```json
{
    "id": 1,
    "status": "confirmed",
    "customer": {
        "name": "Aisha Khan"
    }
}
```

### Create

```javascript
fetch('/api/bookings/', {
    method: 'POST',
    body: JSON.stringify({
        customer_id: 1,
        status: 'pending'
    })
})
```
```json
{
    "id": 3,
    "status": "pending",
    "customer_id": 1
}
```

### Update

```javascript
fetch('/api/bookings/1', {
    method: 'PUT',
    body: JSON.stringify({
        status: 'completed'
    })
})
```
```json
{
    "id": 1,
    "status": "completed"
}
```

### Delete

```javascript
fetch('/api/bookings/1', { method: 'DELETE' })
```
```json
{
    "deleted": true
}
```

---

## Advanced Querying

All query options are sent in the request body.

### Field Selection

```javascript
fetch('/api/bookings/', {
    method: 'GET',
    body: JSON.stringify({
        fields: 'id, status, customer.name'
    })
})
```
```json
{
    "results": {
        "1": {
            "id": 1,
            "status": "confirmed",
            "customer": {
                "name": "Aisha Khan"
            }
        }
    }
}
```

### Nested Relations

```javascript
{
    fields: 'id, customer.name, customer.address.city'
}
```
```json
{
    "results": {
        "1": {
            "id": 1,
            "customer": {
                "name": "Aisha Khan",
                "address": {
                    "city": "Sydney"
                }
            }
        }
    }
}
```

### Wildcard Fields

```javascript
{
    fields: '*, customer.*'
}
```

### Filtering — Exact Match

```javascript
{
    filters: {
        status: 'confirmed'
    }
}
```
```json
{
    "results": {
        "1": {
            "id": 1,
            "status": "confirmed"
        }
    }
}
```

### Filtering — Comparison

```javascript
{
    filters: {
        'price.gte': 50,
        'price.lte': 200
    }
}
```

### Filtering — Text Search

```javascript
{
    filters: {
        'name.icontains': 'khan'
    }
}
```

### Filtering — List Membership

```javascript
{
    filters: {
        'status.in': ['pending', 'confirmed']
    }
}
```

### Filtering — Null Check

```javascript
{
    filters: {
        'assignee.isnull': true
    }
}
```

### Filtering — Date Range

```javascript
{
    filters: {
        'created_at.gte': '2024-01-01',
        'created_at.lte': '2024-12-31'
    }
}
```

### Filtering — Related Fields

```javascript
{
    filters: {
        'customer.vip': true,
        'customer.address.city': 'Sydney'
    }
}
```

### Filtering — OR Conditions

```javascript
{
    filters: {
        or: {
            status: 'pending',
            urgent: true
        }
    }
}
```

### Filtering — NOT Conditions

```javascript
{
    filters: {
        not: {
            status: 'cancelled'
        }
    }
}
```

### Ordering

```javascript
{
    order_by: '-scheduled_date'
}
```
```json
{
    "results": {
        "3": {
            "scheduled_date": "2024-03-15"
        },
        "1": {
            "scheduled_date": "2024-03-10"
        }
    }
}
```

### Pagination

```javascript
{
    limit: 20,
    offset: 0
}
```
```json
{
    "results": {},
    "pagination": {
        "offset": 0,
        "limit": 20,
        "has_more": true,
        "next": {}
    }
}
```

---

## Why Django-Flex?

| Feature            | Django-Flex         | GraphQL           | REST                 |
| ------------------ | ------------------- | ----------------- | -------------------- |
| Learning curve     | Low (Django-native) | High              | Low                  |
| Field selection    | ✅                  | ✅                | ❌ (fixed endpoints) |
| Dynamic filtering  | ✅                  | ✅                | Limited              |
| Built-in security  | ✅                  | Manual            | Manual               |
| Django integration | Native              | Requires graphene | Native               |
| Schema definition  | Optional            | Required          | N/A                  |
| N+1 prevention     | Automatic           | Manual            | Manual               |

---

## Learn More

📖 [Full Documentation](docs/README.md)

## License

MIT
