Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

Django Headless

PyPI version Python versions Django versions License

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 @expose decorator 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 articles
  • POST /api/blog.article/ - Create new article
  • GET /api/blog.article/{id}/ - Retrieve specific article
  • PUT /api/blog.article/{id}/ - Update article
  • PATCH /api/blog.article/{id}/ - Partially update article
  • DELETE /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 configuration
  • PUT /api/config.siteconfiguration/ - Update configuration
  • PATCH /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:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Make your changes
  4. Add tests for your changes
  5. Run the test suite (python manage.py test)
  6. Commit your changes (git commit -m 'Add amazing feature')
  7. Push to the branch (git push origin feature/amazing-feature)
  8. 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

📄 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

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)

Source distribution for django-headless 1.0.0rc3
File Size Uploaded
django_headless-1.0.0rc3.tar.gz 19.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for django-headless 1.0.0rc3
File Interpreter ABI Platform
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
Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page