This release is a pre-release and may not be stable for production use.
django-signals-all
Release candidate.
django-signals-allest en1.0.0rc1: l'API est considérée figée mais n'a pas encore été éprouvée par un usage réel en dehors de ce dépôt. Les retours (issues, cas d'usage, bugs) sont les bienvenus avant de tagger la version1.0.0finale — voir RELEASING.md.
Des signaux Django garantis, peu importe comment vous modifiez vos données.
Le problème
Les opérations en masse (bulk_create, bulk_update, QuerySet.update()) et le SQL
brut (cursor.execute()) exécutent du SQL direct. Elles contournent Model.save() et
ne déclenchent aucun signal Django, ce qui casse l'invalidation de cache, la
réindexation de recherche et la traçabilité des audit logs.
django_signals_all fournit deux niveaux de capture :
- ORM (
django_signals_all.orm) — unQuerySet/Managerpersonnalisé qui émet des signaux applicatifs pourupdate(),bulk_create()etbulk_update(). - SQL brut (
django_signals_all.sql) — un middleware basé surconnection.execute_wrapperqui analyse le SQL exécuté via un curseur et émet un signal sur lesINSERT/UPDATE/DELETE.
Un troisième module, pg_notify (triggers PostgreSQL LISTEN/NOTIFY pour capturer
les mutations effectuées hors de l'application Django), est prévu en roadmap et n'est
pas encore implémenté.
Installation
uv add django-signals-all
# settings.py
INSTALLED_APPS = [
...,
"django_signals_all",
]
MIDDLEWARE = [
...,
"django_signals_all.sql.middleware.RawSQLSignalMiddleware",
]
Module ORM
Le modèle doit utiliser BulkSignalManager comme manager :
# models.py
from django.db import models
from django_signals_all.orm.manager import BulkSignalManager
class Article(models.Model):
title = models.CharField(max_length=200)
status = models.CharField(max_length=20, default="draft")
objects = BulkSignalManager()
# receivers.py
from django.dispatch import receiver
from django.core.cache import cache
from django_signals_all.signals import post_bulk_update, post_bulk_create
from .models import Article
@receiver(post_bulk_update, sender=Article)
def invalidate_cache_on_bulk_update(sender, updated_ids, update_kwargs, using, **kwargs):
"""Déclenché même par Article.objects.filter(...).update(...)."""
cache.delete_many([f"article:{pk}" for pk in updated_ids])
@receiver(post_bulk_create, sender=Article)
def index_on_bulk_create(sender, objects, using, **kwargs):
"""Déclenché après Article.objects.bulk_create([...])."""
search_engine.index_many(objects)
Manager.bulk_update() découpe ses mises à jour en plusieurs requêtes SQL internes
(batch_size) ; ces requêtes internes n'émettent pas post_bulk_update — seul un
post_bulk_model_update unique et agrégé est envoyé, avec l'ensemble des instances et
des champs modifiés :
from django_signals_all.signals import post_bulk_model_update
@receiver(post_bulk_model_update, sender=Order)
def track_bulk_status_change(sender, updated_instances, fields_updated, using, **kwargs):
if "status" in fields_updated:
for order in updated_instances:
audit_log.record(order)
Tous les signaux ORM sont envoyés via transaction.on_commit() : ils ne sont jamais
émis pour une mutation finalement annulée par un rollback.
Module SQL brut
Le signal raw_sql_executed utilise le nom de la table comme sender, pas comme
un kwarg de filtrage arbitraire (@receiver(signal, table_name=...) n'est pas une API
Django valide — receiver()/Signal.connect() ne filtrent que sur sender) :
import logging
from django.dispatch import receiver
from django_signals_all.signals import raw_sql_executed
logger = logging.getLogger("security")
@receiver(raw_sql_executed, sender="crm_client")
def audit_raw_sql_on_crm_client(sender, operation, sql, params, using, **kwargs):
if operation in ("UPDATE", "DELETE"):
logger.warning("Mutation SQL brute (%s) sur %s : %s", operation, sender, sql)
Configuration
# settings.py
DJANGO_SIGNALS_ALL = {
# Module ORM : récupérer les PK impactées avant un update() en masse.
"FETCH_UPDATED_IDS": True,
"MAX_FETCH_IDS_LIMIT": 10_000,
# Module SQL brut.
"ENABLE_RAW_SQL_INTERCEPTOR": True,
"MONITORED_TABLES": None, # ou une liste blanche, ex. ["users_user", "crm_client"]
"EXCLUDED_TABLES": ["django_session", "django_migrations"],
"SQL_PARSER_ENGINE": "sqlglot", # ou "regex"
}
Si aucun receiver n'est connecté à un signal, la bibliothèque évite le travail supplémentaire correspondant (pas de requête pour récupérer les IDs, pas de parsing SQL).
Limitations connues
- Le moteur
regexne comprend pas les CTE (WITH ... UPDATE ...) : il ne détecte pas la table cible dans ce cas. Utilisez le moteursqlglot(par défaut) si votre application en dépend. - Le parsing SQL est du best-effort : une requête que le moteur configuré ne sait pas
analyser est ignorée silencieusement (avec un log de niveau
warningpoursqlglot), jamais une exception propagée à l'application. - Le module
pg_notifyn'existe pas encore dans cette version.
Développement
uv sync --group dev
uv run ruff check src tests
uv run ruff format --check src tests
uv run mypy
# SQLite (par défaut, pas de dépendance externe)
uv run pytest --cov=django_signals_all --cov-report=term-missing
# PostgreSQL et MySQL (nécessite Docker)
docker compose up -d
DSA_TEST_DB=postgres uv run pytest
DSA_TEST_DB=mysql uv run pytest
La CI (.github/workflows/ci.yml) exécute lint, typecheck (mypy strict) et la suite
complète sur les trois SGBD à chaque push.
Voir CONTRIBUTING.md pour contribuer, CHANGELOG.md pour l'historique des versions, et RELEASING.md pour le process de publication.
Licence
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file django_signals_all-1.0.0rc2.tar.gz.
File metadata
- Download URL: django_signals_all-1.0.0rc2.tar.gz
- Upload date:
- Size: 71.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
297191d233543aec7d80ff735b476a095cc59828242f7c0e0931ccf1d6fa10ec
|
|
| MD5 |
86be2888315e0efee163094f608f5a77
|
|
| BLAKE2b-256 |
cce64dc4498f8dc7d565edc6da81b49c45f0b63c7e1e29d96ac43c1cad3951a3
|
Provenance
The following attestation bundles were made for django_signals_all-1.0.0rc2.tar.gz:
Publisher:
publish.yml on alzeph/django-signals-all
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
django_signals_all-1.0.0rc2.tar.gz -
Subject digest:
297191d233543aec7d80ff735b476a095cc59828242f7c0e0931ccf1d6fa10ec - Sigstore transparency entry: 2456095921
- Sigstore integration time:
-
Permalink:
alzeph/django-signals-all@b7e5565250a3b55385d8b96acdb83a2fb9a02465 -
Branch / Tag:
refs/tags/v1.0.0rc2 - Owner: https://github.com/alzeph
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@b7e5565250a3b55385d8b96acdb83a2fb9a02465 -
Trigger Event:
release
-
Statement type:
File details
Details for the file django_signals_all-1.0.0rc2-py3-none-any.whl.
File metadata
- Download URL: django_signals_all-1.0.0rc2-py3-none-any.whl
- Upload date:
- Size: 12.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
50c7d4be32482ab0a54bb425fec9514e59922da9fa63b5ff00399ef5fb970a6d
|
|
| MD5 |
cecddb41532293a32af8764934ab27e2
|
|
| BLAKE2b-256 |
0e8eb151e5ca23a4ac495e8cbc1a44714dd93238b8b199bd95c8149c610c32c4
|
Provenance
The following attestation bundles were made for django_signals_all-1.0.0rc2-py3-none-any.whl:
Publisher:
publish.yml on alzeph/django-signals-all
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
django_signals_all-1.0.0rc2-py3-none-any.whl -
Subject digest:
50c7d4be32482ab0a54bb425fec9514e59922da9fa63b5ff00399ef5fb970a6d - Sigstore transparency entry: 2456095944
- Sigstore integration time:
-
Permalink:
alzeph/django-signals-all@b7e5565250a3b55385d8b96acdb83a2fb9a02465 -
Branch / Tag:
refs/tags/v1.0.0rc2 - Owner: https://github.com/alzeph
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@b7e5565250a3b55385d8b96acdb83a2fb9a02465 -
Trigger Event:
release
-
Statement type: