Skip to content

Persist List State in URL & Preserve Navigation After Edit/Create#772

Open
Alwinator wants to merge 2 commits into
jowilf:mainfrom
Alwinator:url-filter-improvements
Open

Persist List State in URL & Preserve Navigation After Edit/Create#772
Alwinator wants to merge 2 commits into
jowilf:mainfrom
Alwinator:url-filter-improvements

Conversation

@Alwinator
Copy link
Copy Markdown
Contributor

Summary

This PR fixes three related UX issues around DataTables list state persistence and post-edit/create navigation:

  1. Column visibility is now persisted in the URL — Hidden columns are saved as a columns query parameter (comma-separated indices of hidden columns). When the page is reloaded or the URL is shared, column visibility state is restored.

  2. SearchBuilder filters are now correctly applied on page load — Previously, opening a URL with searchBuilder query params would display the filter UI correctly but not actually apply the filter to the data. Now, after DataTables initialization completes, table.searchBuilder.rebuild() is called with the loaded criteria, followed by a table.draw() to trigger the server-side filter.

  3. Edit/Create "Save" now returns to the previous list page — When navigating to edit or create from a filtered/paginated list, the current list URL is passed as a returnTo query parameter. After saving, the user is redirected back to the exact list state (page, filters, search, column visibility) instead of the bare list URL.

Changes

JavaScript (starlette_admin/statics/js/list.js)

  • Added loadedSearchBuilder, loadedHiddenColumns, and isRestoring module-level variables to coordinate deferred state restoration.
  • stateSaveCallback: Serializes hidden column indices into the columns URL param. Suppressed during restoration via the isRestoring flag.
  • stateLoadCallback: Parses columns param to restore column visibility. Stores loaded searchBuilder and hidden column indices for deferred application in initComplete.
  • initComplete: Rebuilds SearchBuilder from URL state, then restores page position and hidden columns in a deferred draw callback. Appends returnTo (using location.pathname + location.search) to the "New" create button.
  • drawCallback: Appends returnTo=<current list path+search> to all .row-actions-container a[href*='/edit/'] links.

Python (starlette_admin/base.py)

  • Added _validate_return_url() method that ensures returnTo is a relative path starting with the admin's base_url (prevents open redirect attacks).
  • _render_edit(): Reads returnTo from query params (GET) or form data (POST). Uses it as the redirect target on "Save".
  • _render_create(): Same behavior as edit.

Python (starlette_admin/contrib/sqla/view.py)

  • Added searchable_relation_fields: Optional[Dict[str, List[str]]] = None class attribute. This fixes an AttributeError ("object has no attribute 'searchable_relation_fields'") caused by the "Add Foreign Key filters" feature accessing self.searchable_relation_fields in find_all/count without declaring it on the class.

Templates

  • edit.html / create.html: Added hidden <input name="returnTo"> field. Cancel button now uses return_url instead of hardcoded list URL.
  • list.html: Bumped list.js cache version to v=4.

Tests (tests/test_return_url.py)

  • 9 new tests covering:
    • Valid returnTo redirects correctly after edit/create
    • Malicious external URLs and protocol-relative URLs are rejected (falls back to list)
    • Missing returnTo defaults to list URL
    • _continue_editing ignores returnTo
    • GET requests pass returnTo to template context

Security

The returnTo parameter is validated server-side:

  • Must start with the admin's base_url (e.g. /admin/)
  • Must not start with // (protocol-relative URLs)
  • External URLs (with scheme) are rejected
  • Falls back to the standard list URL if validation fails

@Alwinator Alwinator changed the title Persist List State in URL & Preserve Navigation After Edit/Create feat: Persist List State in URL & Preserve Navigation After Edit/Create May 27, 2026
@Alwinator Alwinator changed the title feat: Persist List State in URL & Preserve Navigation After Edit/Create Persist List State in URL & Preserve Navigation After Edit/Create May 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant