Metadata-Version: 2.4
Name: django-pgconstraints
Version: 0.2.0
Summary: Declarative PostgreSQL constraint triggers for Django
Project-URL: Homepage, https://github.com/oliverhaas/django-pgconstraints
Project-URL: Documentation, https://oliverhaas.github.io/django-pgconstraints/
Project-URL: Repository, https://github.com/oliverhaas/django-pgconstraints.git
Project-URL: Changelog, https://oliverhaas.github.io/django-pgconstraints/reference/changelog/
Author-email: Oliver Haas <ohaas@e1plus.de>
License: MIT
License-File: LICENSE
Keywords: constraints,django,postgresql,triggers
Classifier: Development Status :: 4 - Beta
Classifier: Framework :: Django
Classifier: Framework :: Django :: 6.0
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.14
Classifier: Typing :: Typed
Requires-Python: >=3.14
Requires-Dist: django-pgtrigger>=4.17.0
Requires-Dist: django<7,>=6.0
Description-Content-Type: text/markdown

# django-pgconstraints

[![PyPI version](https://img.shields.io/pypi/v/django-pgconstraints.svg?style=flat)](https://pypi.org/project/django-pgconstraints/)
[![Python versions](https://img.shields.io/pypi/pyversions/django-pgconstraints.svg)](https://pypi.org/project/django-pgconstraints/)
[![CI](https://github.com/oliverhaas/django-pgconstraints/actions/workflows/ci.yml/badge.svg)](https://github.com/oliverhaas/django-pgconstraints/actions/workflows/ci.yml)

Declarative PostgreSQL constraint triggers for Django, with foreign-key
traversal that the built-in constraints can't express.

`UniqueConstraint`, `CheckConstraint`, and `GeneratedField` in Django 6.0
compile down to plain PostgreSQL constraints — so they can only reference
columns on the same table. `django-pgconstraints` provides three trigger-based
classes that accept `F("related__field")` expressions and compile to PL/pgSQL
triggers via [django-pgtrigger](https://github.com/Opus10/django-pgtrigger).
Their API mirrors the Django equivalents to keep the mental model familiar.

```python
from django.db.models import F, Q
from django_pgconstraints import CheckConstraintTrigger

class OrderLine(models.Model):
    product = models.ForeignKey(Product, on_delete=models.CASCADE)
    quantity = models.IntegerField()

    class Meta:
        triggers = [
            CheckConstraintTrigger(
                condition=Q(quantity__lte=F("product__stock")),
                name="orderline_qty_lte_stock",
            ),
        ]
```

## What's in the box

- **`UniqueConstraintTrigger`** — unique constraint whose `fields=`
  accepts FK chains (`"book__author"`). Also supports Django expressions,
  partial conditions, `nulls_distinct`, and deferred timing.
- **`CheckConstraintTrigger`** — check constraint whose `Q` can cross
  foreign keys (`Q(quantity__lte=F("product__stock"))`).
- **`GeneratedFieldTrigger`** — a computed column whose expression can
  reference related rows. The package automatically installs reverse
  triggers so the value stays in sync when that related data changes,
  and piggybacks `RETURNING` on `save()` / `bulk_create()` so the
  computed value lands on the Python instance with no extra query.

Each trigger also integrates with Django's `Model.full_clean()` so ORM-level
validation still catches violations before they hit the database, and each
accepts `violation_error_code` / `violation_error_message` so the errors
raised by `full_clean()` match your application's error-handling conventions.

## Install

```bash
pip install django-pgconstraints
# or
uv add django-pgconstraints
```

Add `pgtrigger` and `django_pgconstraints` to `INSTALLED_APPS`:

```python
INSTALLED_APPS = [
    ...
    "pgtrigger",
    "django_pgconstraints",
    ...
]
```

Triggers go in `Meta.triggers` (a django-pgtrigger extension), **not**
`Meta.constraints`. The package ships a system check
(`pgconstraints.E001`) that flags the common mistake.

## Requirements

- Python 3.14+
- Django 6.0
- PostgreSQL (any currently supported version)
- django-pgtrigger 4.17+

## Documentation

Full documentation at [oliverhaas.github.io/django-pgconstraints](https://oliverhaas.github.io/django-pgconstraints/).

Runnable examples live in [`examples/`](examples/):

- [`examples/simple/`](examples/simple/) — cross-publisher unique chapter names
- [`examples/full/`](examples/full/) — inventory + purchase orders exercising all three triggers

## License

MIT
