Metadata-Version: 2.5
Name: django-datastar
Version: 0.2.0
Summary: Request metadata, CSRF bridge, and staff-only signal diagnostics for Django and Datastar
Project-URL: Changelog, https://django-datastar.readthedocs.io/en/latest/changelog.html
Project-URL: Documentation, https://django-datastar.readthedocs.io/
Project-URL: Repository, https://github.com/MarcusL11/django_datastar
Project-URL: Issues, https://github.com/MarcusL11/django_datastar/issues
Author-email: "Marcus A. Lee" <hello@marcusalee.com>
License-Expression: MIT
License-File: LICENSE
Classifier: Development Status :: 3 - Alpha
Classifier: Framework :: Django
Classifier: Framework :: Django :: 5.2
Classifier: Framework :: Django :: 6.0
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Typing :: Typed
Requires-Python: >=3.12
Requires-Dist: django<6.1,>=5.2
Provides-Extra: docs
Requires-Dist: furo>=2024.8.6; extra == 'docs'
Requires-Dist: sphinx>=8.1; extra == 'docs'
Provides-Extra: tests
Requires-Dist: django-stubs>=5.2; extra == 'tests'
Requires-Dist: mypy>=1.15; extra == 'tests'
Requires-Dist: pytest-django>=4.10; extra == 'tests'
Requires-Dist: pytest>=8.3; extra == 'tests'
Description-Content-Type: text/markdown

<p align="center">
  <picture>
    <source
      media="(prefers-color-scheme: dark)"
      srcset="https://raw.githubusercontent.com/MarcusL11/django_datastar/main/docs/_static/django-datastar-logo-dark.svg"
    >
    <img
      src="https://raw.githubusercontent.com/MarcusL11/django_datastar/main/docs/_static/django-datastar-logo.svg"
      alt="django-datastar"
      width="720"
    >
  </picture>
</p>

# django-datastar

## Unofficial project

`django-datastar` is an independent, unofficial community plugin. It is not
affiliated with, endorsed by, sponsored by, or maintained by the Datastar
project or Star Federation. The Datastar name is used solely to describe
compatibility with Datastar attributes and expressions.

`django-datastar` provides small, focused integration points between Django and
[Datastar](https://data-star.dev/):

- exact request classification through `Datastar-Request: true`.
- sync and async middleware-backed `request.datastar` metadata.
- an opt-in bridge that supplies Django CSRF tokens to qualifying Datastar
  backend-action requests.
- an opt-in staff-only view of current browser signals on selected pages.

It does not provide Datastar response or SSE APIs. Use
[`datastar-py`](https://github.com/starfederation/datastar/tree/main/sdk/python)
as a companion when those APIs are needed.

> The `Datastar-Request` header is client-controlled metadata. Never use it for
> authentication, authorization, permissions, or a CSRF bypass.

## Installation

Add the package to a uv-managed project:

```console
uv add django-datastar
```

Alternatively, install it with pip:

```console
python -m pip install django-datastar
```

To prepare a development checkout, run `uv sync --group dev` from the repository
root. Without uv, install the checkout with `python -m pip install .`.

Add the middleware before Django's CSRF middleware:

```python
MIDDLEWARE = [
    # ...
    "django_datastar.middleware.DatastarMiddleware",
    "django.middleware.csrf.CsrfViewMiddleware",
    # ...
]
```

Use either the helper or the attached details in a view:

```python
from django.http import HttpResponse
from django_datastar import DatastarHttpRequest


def update(request: DatastarHttpRequest) -> HttpResponse:
    if request.datastar:
        return HttpResponse("Datastar request")
    return HttpResponse("ordinary request")
```

`DatastarHttpRequest` is an annotation for requests processed by the middleware;
Django still creates the actual request object.

If your project already defines a custom request type, declare the middleware
attribute on that type instead:

```python
from django.http import HttpRequest as DjangoHttpRequest
from django_datastar import DatastarDetails


class HttpRequest(DjangoHttpRequest):
    datastar: DatastarDetails
```

Views can then use the project's `HttpRequest` annotation. This follows the
custom-request pattern recommended by `django-stubs`.

When middleware-backed request details are not needed, classify a request directly:

```python
from django.http import HttpRequest
from django.http import HttpResponse
from django_datastar import is_datastar


def update(request: HttpRequest) -> HttpResponse:
    if is_datastar(request):
        return HttpResponse("Datastar request")
    return HttpResponse("ordinary request")
```

## Automatic CSRF headers

The bridge is optional. Add the app when you want its template tag and static
module:

```python
INSTALLED_APPS = [
    # ...
    "django_datastar",
]
```

Load the tag before your chosen Datastar bundle:

```django
{% load django_datastar static %}

{% datastar_csrf %}
<script type="module" src="{% static 'js/datastar.js' %}"></script>
```

For nonce-based Content Security Policies:

```django
{% datastar_csrf nonce=request.csp_nonce %}
```

The tag calls Django's CSRF token machinery and emits a masked token in the DOM,
so it works with `CSRF_COOKIE_HTTPONLY=True`. The external module reads that token
at request time and injects `X-CSRFToken` only when all of these conditions hold:

- the effective method is unsafe;
- `Datastar-Request` is exactly `true`;
- the effective target is same-origin;
- the request mode is compatible with `same-origin`;
- no CSRF header was supplied explicitly; and
- a DOM token is present.

Django's `CsrfViewMiddleware` remains solely responsible for validation. The
bridge assumes Datastar resolves `window.fetch` at request time; see the
[compatibility documentation](https://django-datastar.readthedocs.io/en/latest/compatibility.html)
before upgrading Datastar.

## Signal diagnostics

On selected pages, authenticated, active staff can use an optional floating view of raw Datastar signals.
The view also works with `DEBUG=False`. Keep private values out of browser signals.
See the [signal diagnostics guide](https://django-datastar.readthedocs.io/en/latest/signal-diagnostics.html) for setup, tested bundle support, CSP assumptions, and limits.

![Open signal panel with a search for the enabled signal](https://raw.githubusercontent.com/MarcusL11/django_datastar/main/docs/_static/signal-diagnostics.png)

## Example application

A small Django project shows request metadata, CSRF-protected Datastar POSTs,
and a staff-only signal view. See
[the example guide](example/README.rst) and run it from the repository root:

```console
uv run python example/manage.py migrate
uv run python example/manage.py createsuperuser
uv run python example/manage.py runserver
```

## Documentation

Build the documentation locally with:

```console
python -m pip install ".[docs]"
sphinx-build -W --keep-going -b html docs docs/_build/html
```

For a live-reloading documentation server during development, run:

```console
uv run --with sphinx-autobuild sphinx-autobuild docs docs/_build/html --open-browser
```

The server rebuilds when documentation files change and is available at
<http://127.0.0.1:8000>. Stop it with `Ctrl+C`.

## Development

```console
uv sync --group dev
uv run pytest
uv run python example/manage.py check
(cd example && uv run python manage.py test)
node --test tests/test_datastar_csrf.mjs
node --test tests/test_signal_filter.mjs
node --test tests/browser/example-1.0.4.test.mjs
uv run ruff check .
uv run ruff format --check .
uv run mypy
```

The browser check uses Chrome or Chromium and the pinned Datastar 1.0.4 test bundle.
Node is contributor and CI tooling only. It is not a runtime dependency for
Django applications.

## AI-assisted development

Large language models (LLMs) were used to help generate portions of this
project's code and documentation. All LLM-assisted content was reviewed and
approved by the author, who remains responsible for the final work.

## License

MIT
