Skip to main content

Django & PostgreSQL-based Directed Acyclic Graphs

PyPI Python versions Django versions Documentation License: MIT

Model directed acyclic graphs in Django without paying for the traversal one query at a time.

A lot of graph libraries walk a hierarchy level by level, firing a query for each generation. This one pushes the whole traversal down into PostgreSQL with recursive Common Table Expressions (CTEs), so reading every descendant of a node, every ancestor, or the path between two nodes is a single query regardless of how deep the graph runs.

The catch is portability. That speed comes from Postgres-specific SQL, so this library runs on PostgreSQL only. SQLite, MySQL, and the rest are not supported, and that is unlikely to change.

Is this the right package?

It is a good fit if you want to build and manipulate DAGs that live in your database: add and remove edges, walk ancestors and descendants, find paths, and run the usual DAG algorithms directly against your tables.

It is the wrong fit if you mainly want graph analysis or visualization. If your graph fits in memory and you just want to run algorithms over it, NetworkX or rustworkx will serve you better. When you do need them, the transforms extra hands your data straight to either one.

A quick taste

Define an edge model first, then the node model that connects through it:

from django.db import models
from django_postgresql_dag.models import node_factory, edge_factory


class Edge(edge_factory("Node")):
    pass


class Node(node_factory(Edge)):
    name = models.CharField(max_length=100)

    def __str__(self):
        return self.name

Then wire up a graph and ask it questions:

root = Node.objects.create(name="root")
a = Node.objects.create(name="a")
b = Node.objects.create(name="b")
c = Node.objects.create(name="c")

root.add_child(a)
root.add_child(b)
a.add_child(c)
b.add_child(c)        # c has two parents; that is what makes this a DAG and not a tree

root.descendants()    # every node below root, one query deep or fifty deep
c.ancestors()         # everything above c
root.path(c)          # a route from root down to c
c.is_leaf()           # True

Adding an edge that would close a loop raises an error by default, so the graph stays acyclic without you policing it. You can relax that (and the duplicate/redundant-edge checks) per model if you have a reason to.

Install

pip install django-postgresql-dag

For NetworkX, rustworkx, and JSON export:

pip install django-postgresql-dag[transforms]

Configuration

Graph traversals stop at a maximum depth so a runaway query cannot wander forever. The default is 20. To change it project-wide:

# settings.py
DJANGO_POSTGRESQL_DAG_MAX_DEPTH = 50

You can still override it on any individual call with max_depth=N.

What you can do with a node

Traversal and paths: ancestors(), descendants(), clan(), path(target), connected_graph(), plus the matching edge queries (ancestors_edges(), descendants_edges(), clan_edges()).

Structure and relationships: roots(), leaves(), siblings(), partners(), ancestors_tree(), descendants_tree().

Mutations: add_child(), remove_child(), add_parent(), remove_parent().

Predicates: is_root(), is_leaf(), is_island(), is_ancestor_of(), is_descendant_of().

Heavier algorithms: depth annotation, topological sort, all-paths enumeration, lowest common ancestor, weighted shortest path, critical (longest) path, transitive reduction, and graph hashing (Weisfeiler-Lehman, via NetworkX).

You can also narrow the part of the graph a traversal searches with disallow_nodes, allow_nodes, disallow_edges, allow_edges, and limiting_edges_set_fk (with edge_type as a shorthand). Manager methods connected_components() and graph_stats() work across the whole graph.

The node reference and edge reference cover every method in detail.

Documentation

Quickstart walks through a full example. The Tutorial explains why the pieces fit together the way they do. Everything else lives in the full documentation.

Roadmap

The issue tracker holds the running checklists of where this is headed.

Credits

Earlier projects and writing this one borrows from:

  1. This blog post on recursive CTEs and topological sort in Postgres
  2. django-dag
  3. django-dag-postgresql
  4. django-treebeard-dag

Release files for django-postgresql-dag 2026.7.1

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-postgresql-dag 2026.7.1
File Size Uploaded
django_postgresql_dag-2026.7.1.tar.gz 68.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for django-postgresql-dag 2026.7.1
File Interpreter ABI Platform
django_postgresql_dag-2026.7.1-py3-none-any.whl Python 3 none any Details

Total release size: 106.4 kB

Release files / django_postgresql_dag-2026.7.1.tar.gz

Download URL django_postgresql_dag-2026.7.1.tar.gz
Size 68.8 kB
Tags Source
SHA-256 checksum
How to use checksums
c8cb26cb856d9827ef0decd35d19ab6c15d1c2ea7dde5205fd3018939f0e7db4
BLAKE2b-256 checksum
How to use checksums
d8274fded76e7ab4554c50652a1076539de0ad65abd574eb964e59aadf0000e1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / django_postgresql_dag-2026.7.1-py3-none-any.whl

Download URL django_postgresql_dag-2026.7.1-py3-none-any.whl
Size 37.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
f40f07a25626ee3a099735410268ea276120094fe34d7a435620f604c7e410b8
BLAKE2b-256 checksum
How to use checksums
57374247dad268a1ef3c84956d2c46add790a18d22221e296027c2f38d593286
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release history Release notifications | RSS feed

This release

2026.7.1 This release

2 release files

0.4.0

2 release files

0.3.2

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.5

2 release files

0.2.4

2 release files

0.2.3

2 release files

0.2.2

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.9

2 release files

0.1.8

2 release files

0.1.7

2 release files

0.1.6

2 release files

0.1.5

2 release files

0.1.4

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

0.0.23

2 release files

0.0.22

2 release files

0.0.21

2 release files

0.0.20

2 release files

0.0.19

2 release files

0.0.18

2 release files

0.0.17

2 release files

0.0.14

2 release files

0.0.13

2 release files

0.0.12

2 release files

0.0.11

2 release files

0.0.10

2 release files

0.0.9

2 release files

0.0.8

2 release files

0.0.7

2 release files

0.0.6

2 release files

0.0.5

2 release files

0.0.4

2 release files

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