# django-pgtrigger
`django-pgtrigger` helps you write [Postgres triggers](https://www.postgresql.org/docs/current/sql-createtrigger.html) for your Django models.
## Why should I use triggers?
Triggers can solve a variety of complex problems more reliably, performantly, and succinctly than application code.
For example,
* Protecting operations on rows or columns (`pgtrigger.Protect`).
* Making read-only models or fields (`pgtrigger.ReadOnly`).
* Soft-deleting models (`pgtrigger.SoftDelete`).
* Snapshotting and tracking model changes ([django-pghistory](https://django-pghistory.readthedocs.io/)).
* Enforcing field transitions (`pgtrigger.FSM`).
* Keeping a search vector updated for full-text search (`pgtrigger.UpdateSearchVector`).
* Building official interfaces (e.g. enforcing use of `User.objects.create_user` and not `User.objects.create`).
* Versioning models, mirroring fields, computing unique model hashes, and the list goes on...
All of these examples require no overridden methods, no base models, and no signal handling.
## Quick start
Install `django-pgtrigger` with `pip3 install django-pgtrigger` and add `pgtrigger` to `settings.INSTALLED_APPS`.
`pgtrigger.Trigger` objects are added to `triggers` in model `Meta`. `django-pgtrigger` comes with several trigger classes, such as `pgtrigger.Protect`. In the following, we're protecting the model from being deleted:
```python
import pgtrigger
class ProtectedModel(models.Model):
"""This model cannot be deleted!"""
class Meta:
triggers = [
pgtrigger.Protect(name="protect_deletes", operation=pgtrigger.Delete)
]
```
When migrations are created and executed, `ProtectedModel` will raise an exception anytime a deletion is attempted.
Let's extend this example further and only protect deletions on inactive objects. In this example, the trigger conditionally runs when the row being deleted (the `OLD` row in trigger terminology) is still active:
```python
import pgtrigger
class ProtectedModel(models.Model):
"""Active object cannot be deleted!"""
is_active = models.BooleanField(default=True)
class Meta:
triggers = [
pgtrigger.Protect(
name="protect_deletes",
operation=pgtrigger.Delete,
condition=pgtrigger.Q(old__is_active=True)
)
]
```
`django-pgtrigger` uses `pgtrigger.Q` and `pgtrigger.F` objects to conditionally execute triggers based on the `OLD` and `NEW` rows. Combining these Django idioms with `pgtrigger.Trigger` objects can solve a wide variety of problems without ever writing SQL. Users, however, can still use raw SQL for complex cases.
Triggers are installed like other database objects. Run `python manage.py makemigrations` and `python manage.py migrate` to install triggers.
If triggers are new to you, don't worry. The [pgtrigger docs](https://django-pgtrigger.readthedocs.io/) cover triggers in more detail and provide many examples.
## Compatibility
`django-pgtrigger` is compatible with Python 3.8 - 3.12, Django 3.2 - 5.0, Psycopg 2 - 3, and Postgres 12 - 16.
## Documentation
[View the django-pgtrigger docs here](https://django-pgtrigger.readthedocs.io/) to learn more about:
* Trigger basics and motivation for using triggers.
* How to use the built-in triggers and how to build custom ones.
* Installing triggers on third-party models, many-to-many fields, and other advanced scenarios.
* Writing conditional triggers.
* Ignoring triggers dynamically and deferring trigger execution.
* Multiple database, schema, and partitioning support.
* Frequently asked questions, common issues, and upgrading.
* The commands, settings, and module.
## Installation
Install `django-pgtrigger` with:
pip3 install django-pgtrigger
After this, add `pgtrigger` to the `INSTALLED_APPS` setting of your Django project.
## Other Material
After you've read the docs, check out [this tutorial](https://wesleykendall.github.io/django-pgtrigger-tutorial/) with interactive examples from a Django meetup talk.
The [DjangoCon 2021 talk](https://www.youtube.com/watch?v=Tte3d4JjxCk) also breaks down triggers and shows several examples.
## Contributing Guide
For information on setting up django-pgtrigger for development and contributing changes, view [CONTRIBUTING.md](CONTRIBUTING.md).
## Primary Authors
- [Wes Kendall](https://github.com/wesleykendall)
## Other Contributors
- @jzmiller1
- @rrauenza
- @ralokt
- @adamchainz
- @danifus
- @kekekekule
- @peterthomassen
- @pfouque
Raw data
{
"_id": null,
"home_page": "https://github.com/Opus10/django-pgtrigger",
"name": "django-pgtrigger",
"maintainer": null,
"docs_url": null,
"requires_python": "<4,>=3.8.0",
"maintainer_email": null,
"keywords": null,
"author": "Wes Kendall",
"author_email": null,
"download_url": "https://files.pythonhosted.org/packages/ad/2e/35ec851296e3dbfe58808576099c98d6c22085f56c0ec301ab1e7cfeba72/django_pgtrigger-4.11.1.tar.gz",
"platform": null,
"description": "# django-pgtrigger\n\n`django-pgtrigger` helps you write [Postgres triggers](https://www.postgresql.org/docs/current/sql-createtrigger.html) for your Django models.\n\n## Why should I use triggers?\n\nTriggers can solve a variety of complex problems more reliably, performantly, and succinctly than application code.\nFor example,\n\n* Protecting operations on rows or columns (`pgtrigger.Protect`).\n* Making read-only models or fields (`pgtrigger.ReadOnly`).\n* Soft-deleting models (`pgtrigger.SoftDelete`).\n* Snapshotting and tracking model changes ([django-pghistory](https://django-pghistory.readthedocs.io/)).\n* Enforcing field transitions (`pgtrigger.FSM`).\n* Keeping a search vector updated for full-text search (`pgtrigger.UpdateSearchVector`).\n* Building official interfaces (e.g. enforcing use of `User.objects.create_user` and not `User.objects.create`).\n* Versioning models, mirroring fields, computing unique model hashes, and the list goes on...\n\nAll of these examples require no overridden methods, no base models, and no signal handling.\n\n## Quick start\n\nInstall `django-pgtrigger` with `pip3 install django-pgtrigger` and add `pgtrigger` to `settings.INSTALLED_APPS`.\n\n`pgtrigger.Trigger` objects are added to `triggers` in model `Meta`. `django-pgtrigger` comes with several trigger classes, such as `pgtrigger.Protect`. In the following, we're protecting the model from being deleted:\n\n```python\nimport pgtrigger\n\nclass ProtectedModel(models.Model):\n \"\"\"This model cannot be deleted!\"\"\"\n\n class Meta:\n triggers = [\n pgtrigger.Protect(name=\"protect_deletes\", operation=pgtrigger.Delete)\n ]\n```\n\nWhen migrations are created and executed, `ProtectedModel` will raise an exception anytime a deletion is attempted.\n\nLet's extend this example further and only protect deletions on inactive objects. In this example, the trigger conditionally runs when the row being deleted (the `OLD` row in trigger terminology) is still active:\n\n```python\nimport pgtrigger\n\nclass ProtectedModel(models.Model):\n \"\"\"Active object cannot be deleted!\"\"\"\n is_active = models.BooleanField(default=True)\n\n class Meta:\n triggers = [\n pgtrigger.Protect(\n name=\"protect_deletes\",\n operation=pgtrigger.Delete,\n condition=pgtrigger.Q(old__is_active=True)\n )\n ]\n```\n\n`django-pgtrigger` uses `pgtrigger.Q` and `pgtrigger.F` objects to conditionally execute triggers based on the `OLD` and `NEW` rows. Combining these Django idioms with `pgtrigger.Trigger` objects can solve a wide variety of problems without ever writing SQL. Users, however, can still use raw SQL for complex cases.\n\nTriggers are installed like other database objects. Run `python manage.py makemigrations` and `python manage.py migrate` to install triggers.\n\nIf triggers are new to you, don't worry. The [pgtrigger docs](https://django-pgtrigger.readthedocs.io/) cover triggers in more detail and provide many examples.\n\n## Compatibility\n\n`django-pgtrigger` is compatible with Python 3.8 - 3.12, Django 3.2 - 5.0, Psycopg 2 - 3, and Postgres 12 - 16.\n\n## Documentation\n\n[View the django-pgtrigger docs here](https://django-pgtrigger.readthedocs.io/) to learn more about:\n\n* Trigger basics and motivation for using triggers.\n* How to use the built-in triggers and how to build custom ones.\n* Installing triggers on third-party models, many-to-many fields, and other advanced scenarios.\n* Writing conditional triggers.\n* Ignoring triggers dynamically and deferring trigger execution.\n* Multiple database, schema, and partitioning support.\n* Frequently asked questions, common issues, and upgrading.\n* The commands, settings, and module.\n\n## Installation\n\nInstall `django-pgtrigger` with:\n\n pip3 install django-pgtrigger\n\nAfter this, add `pgtrigger` to the `INSTALLED_APPS` setting of your Django project.\n\n## Other Material\n\nAfter you've read the docs, check out [this tutorial](https://wesleykendall.github.io/django-pgtrigger-tutorial/) with interactive examples from a Django meetup talk.\n\nThe [DjangoCon 2021 talk](https://www.youtube.com/watch?v=Tte3d4JjxCk) also breaks down triggers and shows several examples.\n\n## Contributing Guide\n\nFor information on setting up django-pgtrigger for development and contributing changes, view [CONTRIBUTING.md](CONTRIBUTING.md).\n\n## Primary Authors\n\n- [Wes Kendall](https://github.com/wesleykendall)\n\n## Other Contributors\n\n- @jzmiller1\n- @rrauenza\n- @ralokt\n- @adamchainz\n- @danifus\n- @kekekekule\n- @peterthomassen\n- @pfouque\n",
"bugtrack_url": null,
"license": "BSD-3-Clause",
"summary": "Postgres trigger support integrated with Django models.",
"version": "4.11.1",
"project_urls": {
"Documentation": "https://django-pgtrigger.readthedocs.io",
"Homepage": "https://github.com/Opus10/django-pgtrigger",
"Repository": "https://github.com/Opus10/django-pgtrigger"
},
"split_keywords": [],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "355c7e94a12fd1e0580f814d73e0934ad17b24ce5a9c716d43a1d0689f799e9a",
"md5": "d532cbc844197d8e1c2ed22ca474c382",
"sha256": "9ef664932b3b7fb06e6dc091e4e292d029c221804469ff42968dad272076915f"
},
"downloads": -1,
"filename": "django_pgtrigger-4.11.1-py3-none-any.whl",
"has_sig": false,
"md5_digest": "d532cbc844197d8e1c2ed22ca474c382",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": "<4,>=3.8.0",
"size": 33242,
"upload_time": "2024-04-06T16:58:21",
"upload_time_iso_8601": "2024-04-06T16:58:21.649772Z",
"url": "https://files.pythonhosted.org/packages/35/5c/7e94a12fd1e0580f814d73e0934ad17b24ce5a9c716d43a1d0689f799e9a/django_pgtrigger-4.11.1-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "ad2e35ec851296e3dbfe58808576099c98d6c22085f56c0ec301ab1e7cfeba72",
"md5": "fba3f8dc740e26d45d068fe7095731d5",
"sha256": "39862f229405c6fe49b33451e13910a70578ba569f7e8bea6ea82049058b371f"
},
"downloads": -1,
"filename": "django_pgtrigger-4.11.1.tar.gz",
"has_sig": false,
"md5_digest": "fba3f8dc740e26d45d068fe7095731d5",
"packagetype": "sdist",
"python_version": "source",
"requires_python": "<4,>=3.8.0",
"size": 29899,
"upload_time": "2024-04-06T16:58:22",
"upload_time_iso_8601": "2024-04-06T16:58:22.939181Z",
"url": "https://files.pythonhosted.org/packages/ad/2e/35ec851296e3dbfe58808576099c98d6c22085f56c0ec301ab1e7cfeba72/django_pgtrigger-4.11.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-04-06 16:58:22",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "Opus10",
"github_project": "django-pgtrigger",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"circle": true,
"tox": true,
"lcname": "django-pgtrigger"
}