djust 1.2.0rc11

Pre-releaseReleased
Install
pip install djust==1.2.0rc11

This is a pre-release. djust 1.2.0 has shipped since.

Part of djust 1.2 — read the 1.2 release notes.

Changed

  • djust admin applies the DjustModelAdmin permission hooks to every page, save and delete, and the default hooks follow Django's model permissions. has_view_permission, has_add_permission, has_change_permission and has_delete_permission are checked before mount (list, add) or per object (change, delete), and again in save, form_valid, confirm_delete and the delete_selected action. The defaults now call user.has_perm() with the model's view_/add_/change_/delete_ codename, as Django's ModelAdmin does. Staff users who aren't superusers now need those permissions; previously any active staff account had full access. The admin index lists only models the user has some permission on.
  • Built-in components HTML-escape the values they render. Every component class, {% ... %} component tag and Rust component renderer now passes interpolated values through conditional_escape, in all CSS-framework variants. HTML passed to a content slot must be marked safe (mark_safe, or another component's rendered output), or it is shown as text: this affects modal/card/sheet/popover/tabs/accordion bodies, headers and footers, icons, and similar slots. href/src values using javascript:, vbscript: or non-image data: render as #. The rich-text editor is the exception: its value is cleaned to an HTML allowlist (formatting kept, scripts and event attributes removed) instead of escaped.
  • The observability endpoints require a project token. Besides DEBUG and a loopback client, a request must carry no proxy headers (X-Forwarded-For, Forwarded, X-Real-IP, X-Forwarded-Host, X-Forwarded-Proto) and must send X-Djust-Observability-Token. The token is derived from SECRET_KEY (override with the DJUST_OBSERVABILITY_TOKEN environment variable) and printed by manage.py djust_observability_token. manage.py djust_mcp sends it automatically; other tools and hand-written requests must add the header.
  • Back-navigation state snapshots are bound to a keyed digest of the session key instead of the key itself. Snapshots issued by earlier versions fail verification once, and the view mounts fresh.

Fixed

  • otp_input works: typing fills the boxes and fires the event (#2975). The component rendered six .otp-digit boxes and a hidden input carrying its dj-change, but nothing on the client drove them. Typing left one digit in the first box and the event never fired. python/djust/components/static/djust_components/otp-input.js advances focus per digit, handles backspace, arrows and paste, and fills the hidden input once every box has a digit, which fires the change. Include it on the page; the catalogue now names it. Also fixed while testing every catalogue preview: multi_select's checkboxes sent only whether a box was ticked, never which one, so no handler could tell; each box now sends option too, in both the class and the {% multi_select %} tag. NotificationCenter never opened, because its dropdown was display: none with nothing to show it; it gains is_open. The catalogue's previews now answer every event they emit: dismiss, close and accept show "Show again", host-acted events say the view received them, and overlays preview open.
  • The HTTP event fallback and djust.call send a valid CSRF token when the cookie is renamed or unreadable (#2977). The client read only a csrfmiddlewaretoken input or a cookie literally named csrftoken, so projects with CSRF_COOKIE_NAME, CSRF_COOKIE_HTTPONLY or CSRF_USE_SESSIONS sent an empty X-CSRFToken and got a 403 whenever an event went out before the WebSocket mounted. The injected bootstrap now emits <meta name="djust-csrf-cookie"> and <meta name="djust-csrf-token">, and a shared window.djust.csrfToken() reads the input, then the configured cookie, then the meta token.
  • Hover and active states on an accent background now use the accent's own text colour. Forty-nine rules in djust_components/components.css and the theming stylesheets drew --accent behind the page's --foreground (or no colour at all), not behind --accent-foreground. With a neutral accent that goes unnoticed; with a vivid one it fails contrast: on djust.org's Workbench theme a hovered accordion trigger measured 1.45:1.
  • A component that needs JavaScript beyond djust's client now says so, and its catalogue preview loads it. Sixteen components ship a script in djust_components/ (countdown.js, scroll-spy.js, connection-status.js, …) that a page must include; nothing said so, and the catalogue did not include them either, so those previews never updated. Twenty components carry a dj-hook that no shipped script implements (SortableList, SignaturePad, MapPicker, ImageCropper, …): their markup renders, but the interaction their docstrings describe is left to the app. describe_component now reports both under client (hook, script, hook_shipped), and each catalogue page loads the script and states either requirement.
  • The component catalogue and the generated reference now say only what the component does. describe_component — which both djust.org/components/ and the docs site's reference render — reported **kwargs as a required parameter on every component, and built its events list by scraping the first example's markup. That missed events emitted in a state the example does not show (notification_center's mark-read and clear, kanban_board's kanban_move) and listed events the example itself wrote into the markup (loading_overlay's demo button, dropdown_menu's per-item events). Events now come from the component's own event / *_event parameters as well as its markup; server-to-client stream_event / loading_event names are excluded. The usage snippets no longer write kwargs a component does not have (self.component.dismissed, NotificationCenter.is_open), and they answer every event the copied example emits. In the previews: dropdown_menu opens and closes (its toggle_menu was unanswered, a server error), the date picker's month arrows move from month=0, form_array adds a row, five components that drew an empty box now show content, the pages that cannot preview say why, and example links stay on the page instead of opening 404s. PromptEditor.render() rendered the prompt as a Django template ("Summarise for .") instead of the editor; it now always renders the editor. The catalogue's Parameters table prints types as you would write them (list | None, not typing.Optional[list]) and shows **kwargs as what Component.__init__ does with it rather than as a parameter named kwargs of type typing.Any; and the "Your view received …" note follows the preview instead of sitting under an open overlay's backdrop.
  • DataTable sorts, filters and groups only on declared columns. on_table_sort, on_table_filter, group-by and expression filters now accept only keys listed in table_columns, checked again where the queryset is built. Sorting is on unless a column sets "sortable": False; filtering needs "filterable": True; table_default_sort is always accepted. Unknown column names are ignored.
  • A LiveView joins its channel groups (view, presence, db_notify) only after its permission checks and on_mount hooks pass. A refused mount, including one inside mount_batch, leaves every group, and presence and client-push messages are no longer delivered to a connection with no mounted view. check_permissions and on_mount hooks now run before on_view_mounted.
  • Buttons inside {% modal %}, {% confirm_dialog %}, ExportDialog and BottomSheet panels now reach the server. Each panel carried an inline onclick="event.stopPropagation()", there to stop an inside click from bubbling to the backdrop's close event. djust delegates dj-click from the LiveView root, though, so the same stopPropagation() swallowed every dj-click inside the panel: the close ×, Export, Cancel, and any button you put in a modal. The close event now sits on a .dj-scrim behind the panel, a sibling rather than an ancestor, so an outside click still closes and an inside click reaches its own handler. The inline handler is gone too, so the markup also works under a strict CSP.
  • Resumable uploads record the session that started them. Resume over WebSocket and the HTTP upload-status endpoint answer only that session; the status endpoint previously returned 404 even to the owner. Uploads started before the upgrade, or whose session key changed (for example at login), restart from the beginning.
  • A reused sticky {% live_render %} child re-runs its view and object permission checks on every parent render, and live_redirect carry-over re-checks object permissions too. A child the user may no longer see is unmounted and the render fails the same way a fresh mount would.
  • Theme cookies that name an unregistered pack or an invalid layout fall back to the defaults, and {% theme_css_link %} URL-encodes its query. An unknown pack now resolves to the session pack, then the configured default. Layout names must match [A-Za-z0-9_-]{1,64}.

All releases · Atom feed