This is a pre-release. djust 1.0.0 has shipped since: read the djust 1.0.0 release notes.
Before you upgrade, read Deprecated below and the upgrade guide.
Tests
- Framework-level invariant pin for #1552
{% if/elif %}+{% include %}swap (3 cases, all PASS on main). The #1552 reporter verified the user-visible bug (post-swap DOM contains BOTH step subtrees) still reproduces on rc7 even after PR #1555's dj-id counter fix. Investigation in this PR confirmed: at the framework level, with the bit-exact template shapes the reporter described, the differ produces correct Remove+Insert patches — including the full{% extends %} + {% block %} + {% if/elif %} + {% include %}inheritance shape. The user-visible bug must live in another layer (WS save block / sticky-child persistence / JS patch application / unsampled interaction); pursuing it requires reporter-side data, not more synthetic-shape framework theorizing (per CLAUDE.md Bug-report triage rule #1 and the multi-reopen rule #1389 / PR #1086 precedent). The 3 new framework-pin tests inpython/tests/test_if_elif_include_swap_framework_pin_1552.py(test_framework_include_swap_emits_correct_remove_then_insert_1552,test_diagnostic_patch_op_summary_1552_include_swap,test_framework_include_swap_with_extends_and_block_1552) lock in the framework's current correctness at these shapes — if a future change regresses any of them, the tests catch it fast. The #1552 issue stays OPEN and will receive a follow-up comment requesting the reporter share a BugCapture URL (iter A feature from PR #1563, shipped the same day) capturingstate_before+state_after+vdom_patchesfrom the moment of the broken transition; comparing their actual patches against the framework reproducers will identify the divergent layer.
Added
djust.bug_capture— share a broken djust transition via a URL fragment a teammate can paste back to reproduce, no source-tree access required (B7 iter A, refs #1552; v1.1.0 Path D). Promotes B7 (Time-travel sharable URLs) from "killer demo idea" in the v1.1 readiness session to a load-bearing v1.1 capability, triggered by the #1552 reporter's upstream-bug-velocity data point ("the gap between 'I see it broken' and 'you can see it broken' is the full source tree"). The v1.1 readiness session recommended Path E (defer until launch-soak data exists) with the hedge "refuse to commit before data exists"; the #1552 filing supplied that data. Iter A (this release) ships the foundation: aBugCapturedataclass holding the 3 minimal fields needed to reproduce a broken transition (state_before,state_after,vdom_patches), anencode()/decode()URL-fragment round-trip using a versioneddjbug1.<base64-urlsafe>wire format, ascrubhook with a ready-madescrub_fields(*names)helper for PII redaction, a wire-visiblescrubbed_fieldslist (names only, never values) so reviewers know what was held back, and anencode_view_state(view, scrub=...)convenience that pulls the latest event snapshot + VDOM patches from a view withtime_travel_enabled = True. Security model is load-bearing: the module docstring leads with a 3-paragraph "READ THIS BEFORE USING" warning; encoded blobs may contain user PII and are NOT authenticated;encode()raisesRuntimeErrorin production (DEBUG=False) unless the deployer explicitly opts in viaDJUST_BUG_CAPTURE_PROD_OPT_IN = True(literalTrueonly, not truthy — defensive against typo-enable); the wire format is JSON + base64-urlsafe, never pickle, and a regression test pins this; the decoder treats all input as untrusted (validates types, requires fields, rejects malformed base64 withvalidate=Trueagainst the urlsafe alphabet, rejects malformed JSON, rejects non-object payload, rejects unknown outer version AND mismatched inner"v"field). Iter B (read-only replay viewer at/__djust__/replay/<blob>+ share button in the debug panel) and iter C (Redis snapshot store +djust replayCLI + framework-levelLiveView.time_travel_excluded_fieldsclass attribute withdjust checkV012 enforcement) are tracked as separate v1.1.0 issues #1561 and #1562. Newpython/djust/bug_capture.pymodule; new docs pagedocs/website/guides/bug-capture.mdlinked from_config.yamlandindex.md. Framework integration trade-off:encode_view_state()takespatchesas an explicit required parameter (the caller obtains them fromview.render_with_diff()and passes in). The original sketch readview._last_vdom_patches/view._last_patches, but PR #1563's Stage 11 reviewer correctly caught (Action #1101) that no framework code actually writes those attributes —render_with_diff()returns patches directly into the WS/SSE/runtime frame paths without stashing them on the view. Iter B (#1561) will add a debug-panel button that callsrender_with_diff()+encode_view_state()in one click, eliminating the caller burden.scrub_fields()scopes to top-level keys only (documented as such) — nested paths likestate["user"]["password"]need a custom callable; iter C (#1562) will add framework-leveltime_travel_excluded_fieldsdeclarative scrub. Covered by 36 regression cases inpython/djust/tests/test_bug_capture.pyacross 5 test classes (TestRoundTrip 6, TestScrub 6, TestDebugGate 4, TestUntrustedInput 10, TestEncodeViewState 10 — the EncodeViewState class grew by 2 after the Stage 11 fix-pass:test_raises_on_malformed_patches_jsonandtest_raises_on_patches_wrong_typepin the new_coerce_patchesboundary), including a gate-off self-test (#254 / #1468) confirming 2 of 4 DEBUG-gate tests fail without the_enforce_prod_gate()call (the other 2 are intentionally tautology-safe: prod-opt-in-allowed exercises the bypass path; decode-regardless tests decode, which is gate-independent).
Deprecated
- django-tenants (schema-per-tenant) integration is now deprecated as a multi-tenancy strategy for djust applications (follow-up to #1556). djust ships its own row-level multi-tenancy in
djust.tenants(subdomain/path/header/session resolvers +TenantMixin/TenantScopedMixin+ tenant-scoped state backends + presence isolation), and this is the supported and recommended path going forward. The external django-tenants library implements schema-per-tenant isolation viaSET search_pathon every request, which is a documented production footgun under ASGI + LiveView — every WebSocket event (tick_interval,push_to_view, presence,@notify_on_save) re-entersTenantMainMiddlewareand issues a Postgres roundtrip, exhausting the connection pool under sustained load (#1556 was the prod 503 incident that motivated this deprecation). Thedjust.tenantsrow-level path does not have this failure mode by construction (noSET search_pathin the per-event path). Existing django-tenants integrations continue to work, but no new ASGI-correctness or LiveView integration work will be done on that path; new applications should not adopt it. A dedicated migration guide is tracked as #1559 for v1.1.0, covering the schema-to-row data migration, code/middleware swap, and rollout strategy. Thedjust.C014system check (also in this release; see### Addedbelow) is the in-product breadcrumb pointing existing django-tenants users at the deprecation + migration. Reflected indocs/website/guides/multi-tenant.md(the "Choosing Your Multi-Tenancy Strategy" section explicitly marks django-tenants as deprecated under djust and framesTENANT_LIMIT_SET_CALLS = Trueas a stopgap, not a fix). Behavior change: framework-level — none. Documentation/messaging change: substantial.
Changed
djust.C014hint anddocs/website/guides/multi-tenant.mdupgraded from soft "consider djust.tenants" framing to hard deprecation framing for django-tenants (follow-up to #1556). The first cut of C014 (shipped in this release; see### Added) described django-tenants as one of two viable strategies. After the deprecation decision (see### Deprecatedabove), the messaging now leads with migration as the recommended path and treatsTENANT_LIMIT_SET_CALLS = Trueas a stopgap rather than a long-term fix. Specific changes: C014's primary warning message now explicitly flags django-tenants as deprecated (visible inmanage.py checkoutput without expanding hints); C014'shintleads with migration todjust.tenants+ link to the strategy guide, then describes theTENANT_LIMIT_SET_CALLS = Truestopgap; C014'sfix_hintreorders to lead with migration and labels the django-tenants config path as a stopgap. The multi-tenant guide's "Choosing Your Multi-Tenancy Strategy" section is rewritten to mark the django-tenants subsection as> **Deprecated.**, lists why (production footgun + scope mismatch with djust's mixins), points at the migration tracking issue, and presents the stopgap settings explicitly inside a "stopgap only; migrate to djust.tenants for long-term support" boundary. Covered by 5 new/updated hint-quality test cases inpython/djust/tests/test_c014_multi_tenant_asgi.py::TestC014HintQuality(16 total, up from 11): hint mentionsdjust.tenants, hint links the strategy guide, hint marks django-tenants deprecated,fix_hintleads with migration and treats the flag as stopgap, and the Warning message itself surfaces the deprecation.
Added
- New system check
djust.C014— flag django-tenants integration as deprecated and warn when the stopgapTENANT_LIMIT_SET_CALLS = Trueis missing (#1556). Surfaces both the deprecation (see### Deprecatedabove) and the misconfiguration that caused a production 503 on djustlive: under ASGI + django-tenants, every WebSocket event re-entersTenantMainMiddleware→set_tenant()→SET search_path. LiveView amplifies this —tick_intervalpolling,push_to_viewre-mounts, presence updates, and@notify_on_savelistener re-mounts each re-enter the middleware. WithoutTENANT_LIMIT_SET_CALLS = True, every re-entry issues a fresh Postgres roundtrip; under load the Postgres pool exhausts and pods serve 503 simultaneously. The check fires when ALL of these hold: (1)django_tenantsis inINSTALLED_APPSORTENANT_MODELis set, (2)ASGI_APPLICATIONis set, (3)TENANT_LIMIT_SET_CALLSis unset orFalse. Emits aDjustWarningwhose primary message explicitly flags django-tenants as deprecated; thehintleads with the migration recommendation (link todocs/website/guides/multi-tenant.mdand tracking issue #1559) and describesTENANT_LIMIT_SET_CALLS = Trueas the stopgap; thefix_hintfollows the same order. Suppressible viaDJUST_CONFIG = {'suppress_checks': ['C014']}. The framework-level safety improvement for users still on django-tenants during the migration window — caching the tenant per WS session at LiveView mount time (option a from #1556) — is tracked separately in #1557 (security-reviewlabel) for v1.1.0. New helper_check_multi_tenant_asgi_set_callsinpython/djust/checks.py. Covered by 16 regression cases inpython/djust/tests/test_c014_multi_tenant_asgi.pyacross 4 classes (trigger conditions, negative cases, suppression by short and full ID, and hint quality — the hint-quality class grew from 3 to 8 across two iterations of strategy-steering then deprecation-framing), including a gate-off self-test (#254 / #1468) confirming behavior-meaningful tests fail without the check.