Metadata-Version: 2.4
Name: django-dagster
Version: 0.3.0
Summary: Django plugin for interacting with a Dagster server
Project-URL: Homepage, https://github.com/rclement/django-dagster
Project-URL: Repository, https://github.com/rclement/django-dagster
Project-URL: Changelog, https://github.com/rclement/django-dagster/blob/main/CHANGELOG.md
Project-URL: Issues, https://github.com/rclement/django-dagster/issues
Author-email: Romain Clement <git@romain-clement.net>
License-Expression: Apache-2.0
License-File: LICENSE
Keywords: admin,dagster,django,orchestration,pipeline
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Web Environment
Classifier: Framework :: Django
Classifier: Framework :: Django :: 4.2
Classifier: Framework :: Django :: 5.2
Classifier: Framework :: Django :: 6.0
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: <3.15,>=3.10
Requires-Dist: dagster-graphql
Requires-Dist: django>=4.2
Description-Content-Type: text/markdown

# django-dagster

[![PyPI](https://img.shields.io/pypi/v/django-dagster.svg)](https://pypi.org/project/django-dagster/)
[![Python Versions](https://img.shields.io/pypi/pyversions/django-dagster?logo=python&logoColor=white)](https://pypi.org/project/django-dagster/)
[![CI/CD](https://github.com/rclement/django-dagster/actions/workflows/ci-cd.yml/badge.svg)](https://github.com/rclement/django-dagster/actions/workflows/ci-cd.yml)
[![License](https://img.shields.io/github/license/rclement/django-dagster)](https://github.com/rclement/django-dagster/blob/main/LICENSE)

A Django plugin for interacting with a [Dagster](https://dagster.io/) server through the Django admin interface.

## Features

- Native Django Admin integration — shows up as a **Dagster** section
- List all jobs from connected Dagster instance
- View runs with status and job filtering
- Trigger new job executions with optional JSON run config
- Cancel running jobs
- Re-execute failed/canceled jobs
- View detailed run metadata (config, tags, event logs)
- Granular permission system using Django's built-in permissions

## Screenshots

| Jobs list | Job detail |
|---|---|
| ![Jobs list](https://raw.githubusercontent.com/rclement/django-dagster/main/docs/screenshots/jobs_list.png) | ![Job detail](https://raw.githubusercontent.com/rclement/django-dagster/main/docs/screenshots/job_detail.png) |

| Trigger job | Trigger success |
|---|---|
| ![Trigger job](https://raw.githubusercontent.com/rclement/django-dagster/main/docs/screenshots/job_trigger.png) | ![Trigger success](https://raw.githubusercontent.com/rclement/django-dagster/main/docs/screenshots/job_trigger_success.png) |

| Runs list | Run detail |
|---|---|
| ![Runs list](https://raw.githubusercontent.com/rclement/django-dagster/main/docs/screenshots/runs_list.png) | ![Run detail](https://raw.githubusercontent.com/rclement/django-dagster/main/docs/screenshots/run_detail.png) |

## Requirements

- Python 3.10+
- Django 4.2+

## Installation

```bash
pip install django-dagster
```

## Configuration

Add `django_dagster` to `INSTALLED_APPS` and set `DAGSTER_URL` in your Django settings:

```python
INSTALLED_APPS = [
    ...
    "django_dagster",
]

DAGSTER_URL = "http://localhost:3000"
```

### Optional: DAGSTER_UI_URL

`DAGSTER_URL` is used for two purposes: API calls to the Dagster server, and generating links in the Django Admin UI. In some setups these need to differ, for example when Dagster runs locally (not exposed to the network) and is proxied through Django via a library like [`django-revproxy`](https://github.com/jazzband/django-revproxy):

```python
# Internal API endpoint (used by the Django backend for GraphQL calls)
DAGSTER_URL = "http://127.0.0.1:3000"

# Browser-accessible URL (used for links rendered in admin templates)
# Can be an absolute URL or a relative path (e.g. a reverse-proxy mount point)
DAGSTER_UI_URL = "/dagit"
```

When `DAGSTER_UI_URL` is set, all links rendered in the admin templates (job links, run links, navigation bar) use it as the base URL instead of `DAGSTER_URL`. When not set, `DAGSTER_URL` is used for both purposes.

Then run migrations to create the permission models:

```bash
python manage.py migrate django_dagster
```

No URL configuration or manual admin registration is needed. Navigate to `/admin/` and look for the **Dagster** section.

## Permissions

Access is governed by standard Django permissions that you can assign to users or groups via the Django admin. Superusers always have full access.

| Permission | Codename | Grants access to |
|---|---|---|
| Can view Job | `view_dagsterjob` | View job list and job detail pages |
| Can view Run | `view_dagsterrun` | View run list and run detail pages |
| Can trigger Dagster jobs | `trigger_dagsterjob` | Trigger/submit a new job run |
| Can cancel Dagster runs | `cancel_dagsterrun` | Cancel a running job |
| Can re-execute Dagster runs | `reexecute_dagsterrun` | Re-execute a completed/failed run |
| Can access the Dagster UI | `access_dagster_ui` | Show direct links to the Dagster UI |

To customise behaviour — for example, granting all staff users full access by default — unregister the defaults and register your own subclass in your project's `admin.py`:

```python
from django.contrib import admin
from django_dagster.admin import DagsterJobAdmin, DagsterRunAdmin
from django_dagster.models import DagsterJob, DagsterRun

def _is_active_staff(request):
    return request.user.is_active and request.user.is_staff

class MyDagsterJobAdmin(DagsterJobAdmin):
    def has_module_permission(self, request):
        return _is_active_staff(request)

    def has_view_permission(self, request, obj=None):
        return _is_active_staff(request)

    def has_trigger_dagsterjob_permission(self, request):
        return _is_active_staff(request)

    def has_access_dagster_ui_permission(self, request):
        return _is_active_staff(request)

class MyDagsterRunAdmin(DagsterRunAdmin):
    def has_module_permission(self, request):
        return _is_active_staff(request)

    def has_view_permission(self, request, obj=None):
        return _is_active_staff(request)

    def has_cancel_dagsterrun_permission(self, request):
        return _is_active_staff(request)

    def has_reexecute_dagsterrun_permission(self, request):
        return _is_active_staff(request)

    def has_access_dagster_ui_permission(self, request):
        return _is_active_staff(request)

admin.site.unregister(DagsterJob)
admin.site.unregister(DagsterRun)
admin.site.register(DagsterJob, MyDagsterJobAdmin)
admin.site.register(DagsterRun, MyDagsterRunAdmin)
```

## Programmatic API

The package exposes Django model-like objects for use outside the admin:

```python
from django_dagster import DagsterJob, DagsterRun

# List all jobs
jobs = DagsterJob.objects.all()

# Get a specific job (requires the repository and location names)
job = DagsterJob.objects.get(
    name="etl_pipeline",
    repository="__repository__",
    location="my_location",
)

# Trigger a job
run_id = job.submit(run_config={"ops": {"my_op": {"config": {"x": 1}}}})

# Get default run config
config = job.get_default_run_config()

# List runs (with optional filtering)
runs = DagsterRun.objects.all()
runs = DagsterRun.objects.filter(job_name="etl_pipeline", statuses=["SUCCESS"])

# Get a specific run
run = DagsterRun.objects.get(run_id="abc123")

# Cancel / re-execute a run
run.cancel()
new_run_id = run.reexecute()

# Get event logs
events = run.get_events()
```

## Demo

A self-contained demo project is available in the [`demo/`](demo/) directory with sample Dagster jobs and pre-configured users. See [`demo/README.md`](demo/README.md) for instructions.

## License

This project is licensed under the Apache License 2.0. See the [LICENSE](LICENSE) file for details.
