A flexible query language for Django - enable frontends to dynamically construct database queries
Project description
Django-Flex
A flexible query language for Django — Enable frontends to dynamically construct database queries with built-in security.
Features
- 🔍 Dynamic Field Selection — Request only the fields you need
- 🔬 Rich Filtering — Full Django ORM operator support
- 📄 Built-in Pagination — Limit/offset with smart cursor support
- 🔐 Layered Security — Row, field, filter, and operation-level access control
- ⚡ N+1 Prevention — Automatic
select_relatedoptimization - 🎯 Django Native — Uses Django's built-in auth (groups, permissions)
Installation
pip install django-flex
# settings.py
INSTALLED_APPS = [
...
'django_flex',
]
Quick Start
Using Django's official documentation models: Blog, Author, Entry.
1. Create a View
# views.py
from datetime import date
from django.db.models import Q
from django_flex import FlexQueryView
from .models import Entry
class EntryQueryView(FlexQueryView):
model = Entry
flex_permissions = {
'staff': {
'rows': lambda user: Q(), # All entries
'fields': ['*', 'blog.*'],
'filters': ['id', 'rating.gte', 'blog.id', 'headline.icontains'],
'order_by': ['-pub_date', 'rating'],
'ops': ['get', 'query'],
},
'authenticated': {
'rows': lambda user: Q(pub_date__lte=date.today()),
'fields': ['id', 'headline', 'pub_date', 'blog.name'],
'filters': ['id', 'headline.icontains'],
'order_by': ['-pub_date'],
'ops': ['get', 'query'],
},
}
2. Add URL Route
# urls.py
from .views import EntryQueryView
urlpatterns = [
path('api/entries/', EntryQueryView.as_view()),
]
3. Query from Frontend
// Query entries
const response = await fetch('/api/entries/', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
fields: 'id, headline, pub_date, blog.name',
filters: { 'rating.gte': 4 },
order_by: '-pub_date',
limit: 20
})
});
// Response
{
"success": true,
"code": "FLEX_OK_QUERY",
"pagination": {"offset": 0, "limit": 20, "has_more": false},
"results": {
"1": {"id": 1, "headline": "Django 5.0 Released", "pub_date": "2024-01-15", "blog": {"name": "Django News"}},
"2": {"id": 2, "headline": "Getting Started Guide", "pub_date": "2024-01-14", "blog": {"name": "Tutorials"}}
}
}
Query Language
Field Selection
fields: 'id, headline' // Specific fields
fields: '*' // All model fields
fields: '*, blog.*' // All fields + related
fields: 'id, blog.name, blog.tagline' // Nested fields
Filtering
// Simple equality
filters: { rating: 5 }
// Operators
filters: { 'rating.gte': 4 } // Greater than or equal
filters: { 'rating.in': [4, 5] } // In list
filters: { 'headline.icontains': 'django' } // Case-insensitive search
filters: { 'pub_date.gte': '2024-01-01' } // Date comparison
// Composition
filters: { or: { rating: 5, 'number_of_comments.gte': 100 } }
filters: { not: { 'rating.lt': 3 } }
Pagination
limit: 20 // Max results
offset: 40 // Skip first 40
// Response includes next cursor
pagination: {
offset: 40,
limit: 20,
has_more: true,
next: { fields: '...', limit: 20, offset: 60 }
}
Security Configuration
Django-Flex uses Django's built-in auth for role resolution:
superuser→ bypasses all checksstaff→user.is_staff<group_name>→ first Django groupauthenticated→ logged in, no group
# settings.py
DJANGO_FLEX = {
'DEFAULT_LIMIT': 50,
'MAX_LIMIT': 200,
'MAX_RELATION_DEPTH': 2,
'PERMISSIONS': {
'entry': {
'exclude': ['internal_notes'],
'staff': {
'rows': lambda user: Q(),
'fields': ['*', 'blog.*'],
'filters': ['id', 'rating.gte', 'pub_date.gte', 'blog.id'],
'order_by': ['-pub_date', '-rating'],
'ops': ['get', 'query', 'create', 'update', 'delete'],
},
'authenticated': {
'rows': lambda user: Q(pub_date__lte=date.today()),
'fields': ['id', 'headline', 'pub_date', 'rating'],
'filters': ['id'],
'order_by': ['-pub_date'],
'ops': ['get', 'query'],
},
},
},
}
Usage Patterns
Class-Based View
from django_flex import FlexQueryView
class EntryQueryView(FlexQueryView):
model = Entry
flex_permissions = {...}
Decorator
from django_flex import flex_query
@flex_query(
model=Entry,
allowed_fields=['id', 'headline', 'blog.name'],
allowed_filters=['id', 'headline.icontains'],
allowed_actions=['get', 'query'],
)
def entry_query(request, result, query_spec):
return JsonResponse(result.to_dict())
Programmatic
from django_flex import FlexQuery
result = FlexQuery(Entry).execute({
'fields': 'id, headline, blog.name',
'filters': {'rating.gte': 4},
}, user=request.user)
Supported Operators
| Category | Operators |
|---|---|
| Comparison | lt, lte, gt, gte, exact, in, isnull, range |
| Text | contains, icontains, startswith, endswith, regex |
| Date/Time | date, year, month, day, hour, minute, second |
| Composition | and, or, not |
Response Codes
| Code | Description |
|---|---|
FLEX_OK |
Single object retrieved |
FLEX_OK_QUERY |
Query results returned |
FLEX_LIMIT_CLAMPED |
Results returned, limit was reduced |
FLEX_NOT_FOUND |
Object not found |
FLEX_PERMISSION_DENIED |
Access denied |
FLEX_INVALID_FILTER |
Invalid filter syntax |
Documentation
License
MIT License - see LICENSE for details.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file django_flex-26.1.2.tar.gz.
File metadata
- Download URL: django_flex-26.1.2.tar.gz
- Upload date:
- Size: 34.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fa3f4783118aeb009a2baec70832e15746943ab4923f58224dfe779cd752568d
|
|
| MD5 |
cd04b700aafbc1b74e553c225b0a13a1
|
|
| BLAKE2b-256 |
216877c3348a0e00529a0717101c1efd3d473ba5040400499e7cb049f38ab748
|
File details
Details for the file django_flex-26.1.2-py3-none-any.whl.
File metadata
- Download URL: django_flex-26.1.2-py3-none-any.whl
- Upload date:
- Size: 24.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
db2f09df070538a920b670a6c3c10631f828855b1b63c15debb1a591c59d04d7
|
|
| MD5 |
bb3c2e63e3f7f30d417195b9333baa2b
|
|
| BLAKE2b-256 |
edef1517bccbaff2ffa6ed2643997a46cb57a10821c291826184b83329a98779
|