This release is a pre-release and may not be stable for production use.
Django Headless
With Django Headless you quickly create a REST API for your models, making it easy to turn Django into a powerful headless CMS.
✨ Features
- 🎯 Easy configuration: Add
@exposedecorator to any model and get instant REST endpoints - 🤝 Plays nice: Seamlessly integrates with existing Django applications
- 💈 Supports singletons: Special handling for singleton models (settings, configurations, etc.)
- 🔍 Flexible filtering: Optional filtering backend based on Django ORM lookups
- 🛡️ Secure: Inherits Django's security features and permissions system
🚀 Quick Start
Installation
☝️ Django Headless depends on Django and Django Rest Framework.
pip install django-headless
Add to Django Settings
# settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'headless', # Add this
# ... your apps
]
REST_FRAMEWORK = {
# Optional: add the lookup filter backend
"DEFAULT_FILTER_BACKENDS": [
"headless.rest.filters.LookupFilter",
#...
# Optional: add the secret key authentication class
"DEFAULT_AUTHENTICATION_CLASSES": [
"headless.rest.authentication.SecretKeyAuthentication",
#... other DRF config
}
Create Your First Headless Model
# apps/blog/models.py
from django.db import models
from headless import expose
@expose()
class BlogPost(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
published = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
Add URLs
# urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('headless.urls')),
]
That's it! 🎉 Your model is now available via REST API at /api/blog.blogpost/
📖 Usage Examples
Basic Model Registration
# apps/blog/models.py
from django.db import models
from headless import expose
@expose()
class Article(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
author = models.ForeignKey("user.User", on_delete=models.CASCADE)
Generated endpoints:
GET /api/blog.article/- List all articlesPOST /api/blog.article/- Create new articleGET /api/blog.article/{id}/- Retrieve specific articlePUT /api/blog.article/{id}/- Update articlePATCH /api/blog.article/{id}/- Partially update articleDELETE /api/blog.article/{id}/- Delete article
Singleton Models
Perfect for site settings, configurations, or any model that should have only one instance:
# apps/config/models.py
from django.db import models
from headless import expose
@expose(singleton=True)
class SiteConfiguration(models.Model):
site_name = models.CharField(max_length=100)
maintenance_mode = models.BooleanField(default=False)
contact_email = models.EmailField()
class Meta:
verbose_name = "Site Configuration"
Generated endpoints:
GET /api/config.siteconfiguration/- Get configurationPUT /api/config.siteconfiguration/- Update configurationPATCH /api/config.siteconfiguration/- Partial update
Advanced Filtering
Django Headless supports Django ORM lookups for powerful filtering. Add the LookupFilter backend to your default filter backends:
REST_FRAMEWORK = {
"DEFAULT_FILTER_BACKENDS": [
"headless.rest.filters.LookupFilter",
#... other DRF config
}
Filter examples:
# Basic filtering
GET /api/blog.blogpost/?published=true
# Field lookups
GET /api/blog.blogpost/?title__icontains=django
GET /api/blog.blogpost/?created_at__gte=2023-01-01
GET /api/blog.blogpost/?author__username=john
# Multiple filters
GET /api/blog.blogpost/?published=true&created_at__year=2023
Values are automatically cast based on the field type. Booleans can be represented
as true, 1 or on (and false, 0 or off). Multi-value lookups can be comma-separated (e.g. id__in=1,2,3).
Field Expansion & Model Properties (optional)
By default, endpoints use a plain ModelSerializer. To enable ?expand= for
related models and automatic exposure of model properties as read-only fields,
point DEFAULT_SERIALIZER_CLASS at the FlexibleSerializer:
HEADLESS = {
"DEFAULT_SERIALIZER_CLASS": "headless.rest.serializers.FlexibleSerializer",
}
# Return the related author inline
GET /api/blog.article/?expand=author
# Return the articles of a category inline
GET /api/blog.category/1/?expand=articles
Only relations to other exposed models are expandable.
🎛️ Configuration Options
The @expose decorator accepts the following configuration options:
| Option | Type | Default | Description |
|---|---|---|---|
singleton |
bool |
False |
Creates singleton endpoints (no create, list and delete endpoints) |
search_fields |
List[str] |
All the model's CharFields | Allows you to overwrite the allowed search fields. |
Global Settings
# settings.py
HEADLESS = {
"AUTH_SECRET_KEY": None,
"AUTH_SECRET_KEY_HEADER": "X-Secret-Key",
"DEFAULT_SERIALIZER_CLASS": "rest_framework.serializers.ModelSerializer",
"FILTER_EXCLUSION_SYMBOL": "~",
"FILTER_TRUE_VALUES": ["true", "1", "on"],
"FILTER_FALSE_VALUES": ["false", "0", "off"],
"FILTER_NULL_VALUES": ["null", "none", "empty"],
"NON_FILTER_FIELDS": [
"search",
"limit",
"page",
"fields",
"omit",
"expand",
"ordering"
],
}
🛠️ Requirements
- Python 3.12+
- Django 5.0+
- Django REST Framework 3.16+
🤝 Contributing
We welcome contributions! Here's how to get started:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes
- Add tests for your changes
- Run the test suite (
python manage.py test) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Development Setup
# Clone the repository
git clone https://github.com/dwarsbit/django-headless.git
cd django-headless
# Create virtual environment
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
# Install dependencies
poetry install
# Run tests
poetry run test
# Run example project
cd example_project
python manage.py migrate
python manage.py runserver
📚 Documentation
For detailed documentation, visit djangoheadless.org
🐛 Issues & Support
- 🐛 Bug Reports: GitHub Issues
- 💬 Discussions: GitHub Discussions
- 📧 Email: leon@dwarsbit.nl
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
🙏 Acknowledgments
- Built on the shoulders of Django and Django REST Framework
- Inspired by the headless CMS and Jamstack movement
- Thanks to all contributors and the Django community
🔗 Links
Made in Europe 🇪🇺 with 💚 for Django
Metadata
Release files for django-headless 1.0.0rc3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| django_headless-1.0.0rc3.tar.gz | 19.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| django_headless-1.0.0rc3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 43.0 kB
Release files / django_headless-1.0.0rc3.tar.gz
| Download URL | django_headless-1.0.0rc3.tar.gz |
|---|---|
| Size | 19.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
fdafab0b4d1ac27a5eb7a1cb39397841db56d5b600ac6116e67b42b2c033c755
|
|
BLAKE2b-256 checksum How to use checksums |
e811b11a5b28a9a1f94be8a3f08ab83c33856a29e214f9824425adc862661bf8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/2.4.1 CPython/3.14.7 Darwin/27.0.0
|
Release files / django_headless-1.0.0rc3-py3-none-any.whl
| Download URL | django_headless-1.0.0rc3-py3-none-any.whl |
|---|---|
| Size | 23.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d39bff916d62da78bfbd2e7aa9b134d78ea411d53438cd6dc4435d1a9ffdfef2
|
|
BLAKE2b-256 checksum How to use checksums |
f18d4a040012cee430d71ae2c1db0c263dfa7cbfe75b6237b15ac7d1430b89a7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/2.4.1 CPython/3.14.7 Darwin/27.0.0
|