Skip to main content

A Django library for standardized API responses.

Project description

Django Unified Response

PyPI version Python Support Django Support License Tests

Wrap every Django REST Framework response in a clean, consistent, and fully customisable JSON envelope — no boilerplate, no weird edge cases.


✨ Why yet another response wrapper?

  • Zero boilerplate – all views automatically get the same envelope.
  • DRF error‑proof – the messiest validation errors are turned into a predictable, structured format.
  • Pagination‑aware – detects standard, cursor, and custom paginators, keeping metadata tidy.
  • Swagger‑ready – optional drf-spectacular integration generates the exact schemas.
  • Totally customisable – replace the envelope globally by writing a formatter class, without touching the library.

📦 What responses look like

✅ Success

{
    "success": true,
    "data": { "id": 1, "name": "Test Item" },
    "meta": {}
}

❌ Client error (4xx)

{
    "success": false,
    "error": {
        "type": "Fail",
        "code": "validation_error",
        "message": "Input validation failed.",
        "details": [
            { "field": "email", "issue": "Enter a valid email address." }
        ]
    }
}

💥 Server error (5xx)

{
    "success": false,
    "error": {
        "type": "Error",
        "code": "HTTP_500",
        "message": "A server error occurred.",
        "details": null
    }
}

⚙️ Requirements

  • Python 3.10+
  • Django 3.2 / 4.0 / 4.1 / 4.2
  • Django REST Framework 3.12+
  • (Optional) drf‑spectacular for OpenAPI schema generation

🚀 Installation

pip install django-unified-response

With Swagger support

pip install "django-unified-response[swagger]"

🛠 Quick Start

  1. No need to add to INSTALLED_APPS – the package has no models.
  2. Configure Django REST Framework in settings.py:
REST_FRAMEWORK = {
    'DEFAULT_RENDERER_CLASSES': [
        'django_unified_response.renderers.UnifiedJSONRenderer',
        # Keep BrowsableAPIRenderer for the DRF web interface
        'rest_framework.renderers.BrowsableAPIRenderer',
    ],
    'EXCEPTION_HANDLER': 'django_unified_response.handlers.unified_exception_handler',
}

That’s it! Every response is now unified.


🔧 Configuration

All library settings live in the DUR_SETTINGS dictionary:

# settings.py
DUR_SETTINGS = {
    # Formatter class that defines the envelope (default shown)
    "FORMATTER_CLASS": "django_unified_response.formatters.DefaultFormatter",
    # Convert snake_case keys to camelCase
    "CAMELCASE_KEYS": False,
    # Temporarily disable the entire wrapper
    "ENABLE": True,
}
Setting Type Default Description
FORMATTER_CLASS string (import path) "django_unified_response.formatters.DefaultFormatter" Path to a formatter class (see Advanced Customisation)
CAMELCASE_KEYS bool False If True, all response keys become camelCase
ENABLE bool True Set to False to return raw DRF responses globally

🧪 Usage

Success responses

Return a standard DRF Response. The renderer wraps it automatically.

from rest_framework.views import APIView
from rest_framework.response import Response

class MyView(APIView):
    def get(self, request):
        return Response({"id": 1, "name": "Amir"})

Client receives:

{
    "success": true,
    "data": { "id": 1, "name": "Amir" },
    "meta": {}
}

Including metadata

If your response already has "data" and/or "meta" keys, the renderer respects them:

return Response({
    "data": {"items": [...]},
    "meta": {"page": 1, "total": 42}
})

Paginated responses

The renderer auto‑detects DRF pagination (any class that returns "results"), moves the results to data, and puts the rest (count, next, previous, cursor, etc.) under meta.pagination.

Bypassing the wrapper

Use the decorator on any view to keep the raw DRF response:

from django_unified_response.decorators import bypass_unified_response

@bypass_unified_response
class HealthCheckView(APIView):
    def get(self, request):
        return Response({"status": "ok"})

Error responses

Standard DRF exceptions

Raised automatically by serializers — the handler formats them.

serializer.is_valid(raise_exception=True)  # yields a formatted 4xx

Custom library exceptions

Import and raise for business‑logic errors:

from django_unified_response.exceptions import (
    NotFoundException,
    IntegrityException,
    ValidationException,
    AuthenticationFailedException,
)

def get_product(request, pk):
    try:
        product = Product.objects.get(pk=pk)
    except Product.DoesNotExist:
        raise NotFoundException()  # 404, code "not_found"

def create_product(request):
    try:
        ...
    except IntegrityError:
        raise IntegrityException(
            message="SKU already exists.",
            details={"sku": "duplicate"}
        )

📘 Swagger / OpenAPI

Install the [swagger] extra, then set the schema class:

REST_FRAMEWORK = {
    # ...
    'DEFAULT_SCHEMA_CLASS': 'django_unified_response.schema.UnifiedResponseAutoSchema',
}

Your OpenAPI docs will automatically show the unified success/error shapes.


🎨 Advanced Customisation

You can replace the entire envelope by writing your own formatter.

  1. Subclass BaseFormatter (or DefaultFormatter to override only parts):
# my_app/formatters.py
from django_unified_response.formatters import BaseFormatter

class MyFormatter(BaseFormatter):
    def format_success(self, data, meta=None):
        return {"ok": True, "result": data, "extra": meta or {}}

    def format_fail(self, error_code, message, details=None):
        return {
            "ok": False,
            "problem": {
                "code": error_code,
                "what": message,
                "fields": details or [],
            },
        }

    def format_error(self, error_code, message, details=None):
        return {
            "ok": False,
            "problem": {
                "code": error_code,
                "what": "Internal error",
                "trace": message if settings.DEBUG else None,
            },
        }
  1. Point to it in DUR_SETTINGS:
DUR_SETTINGS = {
    "FORMATTER_CLASS": "my_app.formatters.MyFormatter",
}

Every response now follows your own contract.


👩‍💻 Development

Prerequisites

  • uv
  • Python 3.10+

Setup

git clone https://github.com/amirhh-2000/django-unified-response.git
cd django-unified-response
make install-dev

Commands

make install-dev   # install development deps
make test          # run tests
make lint          # ruff linter
make format        # ruff formatter
make security      # bandit security checks
make clean         # remove build artifacts

🤝 Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes and write tests
  4. Run make lint and make test
  5. Update CHANGELOG.md (Keep a Changelog format)
  6. Open a pull request

📄 Changelog

All notable changes are documented in CHANGELOG.md.


📜 License

MIT. See LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django_unified_response-2.0.0.tar.gz (125.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

django_unified_response-2.0.0-py3-none-any.whl (12.0 kB view details)

Uploaded Python 3

File details

Details for the file django_unified_response-2.0.0.tar.gz.

File metadata

  • Download URL: django_unified_response-2.0.0.tar.gz
  • Upload date:
  • Size: 125.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for django_unified_response-2.0.0.tar.gz
Algorithm Hash digest
SHA256 92c9f61da4608369dc3127bd9cad98fac0e7216fb6d6d21092093d53ce988e0e
MD5 050df0ba5594638da7f1af261032429f
BLAKE2b-256 4b0a52c23f15da39684a5331acc26a8ec5fd6b870650e42b6c61e42e2f81eac0

See more details on using hashes here.

File details

Details for the file django_unified_response-2.0.0-py3-none-any.whl.

File metadata

File hashes

Hashes for django_unified_response-2.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 08ef13e0f80eb461c884563a5d3d5c8dc66c9b21ad73cb102b1905ceacc71ff1
MD5 644d6ec2fb179784b10d5c22cd4a4c3e
BLAKE2b-256 ef3c0ac1ea6ea7dbcaf0a0137745d78e94ee2e2f7a8938404f895a3be9ed4b6b

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page