Skip to content

feat: paginate delete blockers - #8238

Draft
grantfitzsimmons wants to merge 70 commits into
mainfrom
issue-7515-2
Draft

grantfitzsimmons wants to merge 70 commits into
mainfrom
issue-7515-2

Conversation

@grantfitzsimmons

@grantfitzsimmons grantfitzsimmons commented Jun 30, 2026 •

Copy link
Copy Markdown
Member

Authored by @melton-jason

Primarily fixes #7515
Also fixes #2231, #5147, #4592

Description

The Problem

The previous implementation of Delete Blockers relied on Django's ORM (see ForeignKey.on_delete and Collector.collect) to collect all records that would prevent object deletion, and add them to a list.
The issue with this implementation is that some objects can have hundreds of thousands (or more) of references that must all be put into this list at the same time. (e.g., Agents through nearly all table's createdByAgent and modifiedByAgent relationships,TreeDef records or high-level Tree nodes, etc.).

Relevant sections of old Delete Blocker code

def protect_with_blockers(collector, field, sub_objs, using):
if hasattr(collector, 'delete_blockers'):
collector.delete_blockers.append((field, sub_objs))
else:
return models.PROTECT(collector, field, sub_objs, using)

def _collect_delete_blockers(obj, using) -> list[dict]:
collector = Collector(using=using)
collector.delete_blockers = []
collector.collect([obj])

This led to a major scalability problem where the amount of memory required to hold all of the record references at once could easily exceed what the server can handle.

See #7515 (comment) for more information

The Solution

To fix the problem of having to hold all references in memory at once, the solution is to only hold a section/portion of total referenced records in memory at once, and let the requester (in most cases the frontend) page through the records to retrieve smaller sections.

This solution utilizes a type of Pagination called Keyset or Cursor Pagination. This type of pagination is far more performant than OFFSET pagination (which scales linearly based on the OFFSET amount).

User Experience

View Parent Resource Button
This was a miscellaneous feature added in the PR that also greatly helps Delete Blockers.
Records that are dependent on some other record will have a button appear on their DataEntry form called "View Parent Resource".
When clicked, the button will display the resource's parent.

parent_resource_button
parent_resource_demo.mov

Basic Delete Blocker Interface
When all of the blocking records reference the "main" record

delete_blocker_basic_demo.mov

Below is a more complicated breakdown for Delete Blockers on an Agent:

delete_blocker_explained

More Complicated Delete Blocker Interface
When there are delete blockers for records that would be deleted alongside the base record

Consider the following case for a CollectionObject where one or more Preparations are being used in Interactions:

delete_blocker_cascade_demo.mov

Below is a more in-depth breakdown of the current UI:

delete_blocker_cascade_explained

Linked Records

Tree Records

@coderabbitai

coderabbitai Bot commented Jun 30, 2026 •

Copy link
Copy Markdown
Contributor

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-7515-2
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@grantfitzsimmons

Copy link
Copy Markdown
Member Author

@melton-jason Could you base this instead on v7_12_0_7_base?

Comment thread specifyweb/backend/delete_blockers/views.py Fixed
@melton-jason
melton-jason changed the base branch from main to v7_12_0_7_base June 30, 2026 12:18
@CarolineDenis CarolineDenis modified the milestones: 7.12.0.7, 7.12.1 Jul 7, 2026
@CarolineDenis CarolineDenis modified the milestones: 7.12.1, 7.12.2 Aug 18, 2026
@CarolineDenis CarolineDenis modified the milestones: 7.12.2, 7.12.3 Sep 3, 2026
@CarolineDenis CarolineDenis modified the milestones: 7.12.2, 7.12.3 Sep 30, 2026
@github-actions

github-actions Bot commented Oct 3, 2026 •

Copy link
Copy Markdown

Warning

One or more dependencies are approaching or past End-of-Life.
Please plan upgrades accordingly.

STATUS=WARNING
NODE_VERSION=20
NODE_CYCLE=20
EOL_DATE=2026-04-30
DAYS_REMAINING=-159

--- Node.js ---
Version: 20
EOL: 2026-04-30
Status: WARNING

STATUS=OK
PYTHON_VERSION=3.12
PYTHON_CYCLE=3.12
EOL_DATE=2028-10-31
DAYS_REMAINING=756

--- Python ---
Version: 3.12
EOL: 2028-10-31
Status: OK

STATUS=WARNING
DJANGO_VERSION=4.2
DJANGO_CYCLE=4.2
EOL_DATE=2026-04-07
DAYS_REMAINING=-182

--- Django ---
Version: 4.2
EOL: 2026-04-07
Status: WARNING


Comment thread specifyweb/backend/delete_blockers/paginate.py Dismissed
Comment thread specifyweb/backend/trees/defaults.py Dismissed
Comment thread specifyweb/backend/delete_blockers/filter.py Fixed
Comment thread specifyweb/backend/delete_blockers/paginate.py Fixed
Comment thread specifyweb/backend/delete_blockers/paginate.py Fixed
Comment thread specifyweb/backend/delete_blockers/validators.py Fixed
Comment thread specifyweb/frontend/js_src/lib/hooks/useScreenSize.tsx Fixed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: 📋Back Log

4 participants