Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

django-signals-all

CI PyPI License: MIT Python 3.12+

Release candidate. django-signals-all est en 1.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 version 1.0.0 finale — 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 :

  1. ORM (django_signals_all.orm) — un QuerySet/Manager personnalisé qui émet des signaux applicatifs pour update(), bulk_create() et bulk_update().
  2. SQL brut (django_signals_all.sql) — un middleware basé sur connection.execute_wrapper qui analyse le SQL exécuté via un curseur et émet un signal sur les INSERT/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 regex ne comprend pas les CTE (WITH ... UPDATE ...) : il ne détecte pas la table cible dans ce cas. Utilisez le moteur sqlglot (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 warning pour sqlglot), jamais une exception propagée à l'application.
  • Le module pg_notify n'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

MIT

Metadata

Release files for django-signals-all 1.0.0rc2

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-signals-all 1.0.0rc2
File Size Uploaded Provenance
django_signals_all-1.0.0rc2.tar.gz 71.4 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for django-signals-all 1.0.0rc2
File Interpreter ABI Platform
django_signals_all-1.0.0rc2-py3-none-any.whl Python 3 none any Details

Total release size: 83.7 kB

Release files / django_signals_all-1.0.0rc2.tar.gz

Download URL django_signals_all-1.0.0rc2.tar.gz
Size 71.4 kB
Tags Source
SHA-256 checksum
How to use checksums
297191d233543aec7d80ff735b476a095cc59828242f7c0e0931ccf1d6fa10ec
BLAKE2b-256 checksum
How to use checksums
cce64dc4498f8dc7d565edc6da81b49c45f0b63c7e1e29d96ac43c1cad3951a3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 13, 2026.

Transparency log

Release files / django_signals_all-1.0.0rc2-py3-none-any.whl

Download URL django_signals_all-1.0.0rc2-py3-none-any.whl
Size 12.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
50c7d4be32482ab0a54bb425fec9514e59922da9fa63b5ff00399ef5fb970a6d
BLAKE2b-256 checksum
How to use checksums
0e8eb151e5ca23a4ac495e8cbc1a44714dd93238b8b199bd95c8149c610c32c4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 13, 2026.

Transparency log

Security for django-signals-all 1.0.0rc2

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for django_signals_all-1.0.0rc2.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 13, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

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