Skip to content

Fixed #10919 -- Added ModelAdmin option to truncate delete confirmation object list.#20903

Open
rodbv wants to merge 3 commits intodjango:mainfrom
rodbv:ticket_10919
Open

Fixed #10919 -- Added ModelAdmin option to truncate delete confirmation object list.#20903
rodbv wants to merge 3 commits intodjango:mainfrom
rodbv:ticket_10919

Conversation

@rodbv
Copy link
Copy Markdown
Contributor

@rodbv rodbv commented Mar 14, 2026

Trac ticket number

ticket-10919

Branch description

The original ticket proposed adding an option to limit or hide the list of related objects shown on the delete confirmation page. This list can become very long, especially for complex object graphs, because related items are rendered recursively. As a result, the confirmation button is pushed far down the page.

image

This PR implements a suggestion by @terminator14 (comment 13) to truncate the list at a given number, which is configurable, and complement the list with …and N more.

The setting is called delete_confirmation_max_display and its default value is None, which means it's an opt-in setting.

class PostAdmin(admin.ModelAdmin):
    delete_confirmation_max_display = 100
    # .... more settings

It renders like this

Shows how a list is complemented with an li having the text ... and 200 more

Dark mode - note that truncation is also applied to nested items

image

This still gives the user a good sense of the impact of the deletion, but keeps the page size and memory usage within reasonable limits.

AI Assistance Disclosure (REQUIRED)

  • No AI tools were used in preparing this PR.
  • If AI tools were used, I have disclosed which ones, and fully reviewed and verified their output.

Used GitHub Copilot to request an initial review, considering the practices found on the codebase, and also to generate HTML for test cases. I fully reviewed and verified all output before submitting this PR. The solution/algorithm is mine.

Checklist

  • This PR follows the contribution guidelines.
  • This PR does not disclose a security vulnerability (see vulnerability reporting).
  • This PR targets the main branch.
  • The commit message is written in past tense, mentions the ticket number, and ends with a period (see guidelines).
  • I have not requested, and will not request, an automated AI review for this PR.
  • I have checked the "Has patch" ticket flag in the Trac system.
  • I have added or updated relevant tests.
  • I have added or updated relevant docs, including release notes if applicable.
  • I have attached screenshots in both light and dark modes for any UI changes.

@github-actions github-actions bot added the no ticket Based on PR title, no linked Trac ticket label Mar 14, 2026
Copy link
Copy Markdown

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello! Thank you for your contribution 💪

As it's your first contribution be sure to check out the patch review checklist.

If you're fixing a ticket from Trac make sure to set the "Has patch" flag and include a link to this PR in the ticket!

If you have any design or process questions then you can ask in the Django forum.

Welcome aboard ⛵️!

@rodbv rodbv force-pushed the ticket_10919 branch 2 times, most recently from f69a687 to 3a4e285 Compare March 14, 2026 09:45
@rodbv rodbv changed the title Ticket 10919 Fixed #10919 Mar 14, 2026
@github-actions github-actions bot removed the no ticket Based on PR title, no linked Trac ticket label Mar 14, 2026
@rodbv rodbv changed the title Fixed #10919 Fixed #10919 -- Truncate list of deleted objects on delete confirmation page Mar 14, 2026
@rodbv rodbv force-pushed the ticket_10919 branch 5 times, most recently from 385d74a to c670447 Compare March 14, 2026 16:29
@rodbv rodbv changed the title Fixed #10919 -- Truncate list of deleted objects on delete confirmation page Fixed #10919 -- Made delete confirmation object list truncation configurable. Mar 14, 2026
@rodbv rodbv changed the title Fixed #10919 -- Made delete confirmation object list truncation configurable. Fixed #10919 -- Added ModelAdmin option to truncate delete confirmation object list. Mar 14, 2026
@rodbv rodbv marked this pull request as ready for review March 14, 2026 16:33
@naranjo-it
Copy link
Copy Markdown

nicely done @rodbv 👏 . I like your solution, but I was wondering if instead of having the plain text "...and N more" you could make it into a link that will show the full results.
Or another idea would be to introduce pagination to call and navigate the results, but not sure how easy would be to integrate pagination into this view.

All optional of course, in my opinion as it is already adds value for this use case :)

@rodbv
Copy link
Copy Markdown
Contributor Author

rodbv commented Mar 15, 2026

nicely done @rodbv 👏 . I like your solution, but I was wondering if instead of having the plain text "...and N more" you could make it into a link that will show the full results. Or another idea would be to introduce pagination to call and navigate the results, but not sure how easy would be to integrate pagination into this view.

All optional of course, in my opinion as it is already adds value for this use case :)

Thanks @naranjo-it! I will check both possibilities, I guess that making the "...and N more" text a link that reloads the page with truncation disabled is pretty straightforward and it would ensure users don't lose the ability to check any specific objects if they want to.

@jacobtylerwalls jacobtylerwalls added the coverage Generate a comment with a coverage diff label Mar 16, 2026
@rodbv
Copy link
Copy Markdown
Contributor Author

rodbv commented Mar 17, 2026

nicely done @rodbv 👏 . I like your solution, but I was wondering if instead of having the plain text "...and N more" you could make it into a link that will show the full results. Or another idea would be to introduce pagination to call and navigate the results, but not sure how easy would be to integrate pagination into this view.
All optional of course, in my opinion as it is already adds value for this use case :)

Thanks @naranjo-it! I will check both possibilities, I guess that making the "...and N more" text a link that reloads the page with truncation disabled is pretty straightforward and it would ensure users don't lose the ability to check any specific objects if they want to.

After some checks, it's not that straighforward because one of the ways to get to the confirmation page is via POST, when a user selects one or more objects in the list view and picks the "delete" action.

So I will leave the PR as it is by now, to avoid adding too much complexity, and see what else is requested.

@django django deleted a comment from github-actions bot Mar 17, 2026
@django django deleted a comment from github-actions bot Mar 17, 2026
Copy link
Copy Markdown
Member

@jacobtylerwalls jacobtylerwalls left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the start here @rodbv, any progress we can make on this problem is going to be very welcomed by folks, I think!


So, my first thought is: I'm assuming an important part of the performance problem is fetching millions of rows from the db, not necessarily just rendering the million <li> rows. Did you look into an approach that would slice the queryset or otherwise limit the collected objects up front?

def list_formatter(item_list, tabs=1):
indent = "\t" * tabs
output = []
truncated_count = 0
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This counter gets reset on each recursion, so I'm getting n * m related objects for a limit of n and m related models.

Copy link
Copy Markdown
Contributor Author

@rodbv rodbv Mar 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's by design, from this perspective: Suppose we have a blog system and we're applying delete_confirmation_max_objects on BlogAdmin, with value 30. From the user's perspective, it means "it want to see at most 30 posts on the confirmation page, and you can truncate the rest". So if there are more than 30 posts, they will consistently see 30 items and then "...and N more".

If we adopt a global counter, and the first post happens to have 100 comments, they would see only 1 post, 29 comments of the first post. If the order of posts change, they may see different numbers of posts.

Having said that I'm not totally against a global counter, if you prefer, I went for the most conservative approach.

Copy link
Copy Markdown
Contributor Author

@rodbv rodbv Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jacobtylerwalls Now that you showed me the case for mixed types on the same level, I am reconsidering this approach, as it may be hiding some types anyway. Maybe make the counter global and have at the end an outdented message like "N objects not displayed" could be better. What do you think?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a great time to go to the forum.

Since new ModelAdmin attributes are only a couple steps lower on the ladder of Debatable Ideas than new settings (😄), and since this ticket was accepted ~17 years ago without a clear statement of "yes, let's add a ModelAdmin attribute", I think it would be good to loop in other voices besides mine.

I would succinctly state:

  • why you are focusing on the front-end truncation solution only (I took it to be that even with a backend solution we'd still want to tune the front-end display?)
  • the issue with mixed types and how we have to choose between a global counter versus n^max_depth, showing an example of the two possible outputs

Finally, we also need to decide what approach harmonizes best with #20945, which implements a backend limit for a very similar use case.

Copy link
Copy Markdown
Contributor Author

@rodbv rodbv Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. I can only post 1 embedded image and 2 links on my post there, as it's my first topic (got an error message when I submitted). So I will upload a preview of the global solution here and link :P

image

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! (I think the forum post link to this 404's for me, maybe take another look?)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I just meant the link to the above image at "[you can see an example here]"

@django django deleted a comment from github-actions bot Mar 17, 2026
@rodbv
Copy link
Copy Markdown
Contributor Author

rodbv commented Mar 18, 2026

Thanks for the start here @rodbv, any progress we can make on this problem is going to be very welcomed by folks, I think!

So, my first thought is: I'm assuming an important part of the performance problem is fetching millions of rows from the db, not necessarily just rendering the million <li> rows. Did you look into an approach that would slice the queryset or otherwise limit the collected objects up front?

get_deleted_objects needs to traverse the full deletion graph to get all the counters it needs for the objects to be directly deleted and also its nested objects, splitting counters by protected, not permitted etc. I couldn't think of a feasible way to optimize this, unless I go for a big rewrite of get_deleted_objects.

My focus on this PR is on the frontend: reduce the amount of data sent to the browser, and the annoyance of scrolling a long way down to reach the "I'm sure" button.

For millions of items it will be a problem indeed, but hopefully for most cases (thousands of items) the db performance is not too bad.

Copy link
Copy Markdown
Member

@jacobtylerwalls jacobtylerwalls left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks great! Just a handful of very minor questions for you about wording and such.

"<li>D</li>",
)

def test_non_iterable_list_subclass(self):
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adds test coverage for type error check on _walk_items.

@rodbv rodbv force-pushed the ticket_10919 branch 4 times, most recently from 81793f7 to 41aa1e8 Compare March 22, 2026 12:58
rodbv added 2 commits March 22, 2026 10:06
…dmin.

The new ModelAdmin.delete_confirmation_max_display attribute allows
limiting the number of related objects shown on the delete confirmation
page. When the limit is reached, a "…and N more." message is shown.

The feature relies on a new truncated_unordered_list template filter
added to django.contrib.admin.templatetags.admin_filters.

Thanks Jacob Tyler Walls for the review, Tobias McNulty for the report,
and terminator14 for the solution suggested.
@github-actions
Copy link
Copy Markdown

Uncovered lines in changed files

-------------
django/contrib/admin/checks.py (100%)
django/contrib/admin/options.py (100%)
django/contrib/admin/templatetags/admin_filters.py (100%)
django/template/defaultfilters.py (100%)
-------------
Total:   58 lines
Missing: 0 lines
Coverage: 100%
-------------

Note: Missing lines are warnings only. Some database-specific lines may not be measured. More information

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

coverage Generate a comment with a coverage diff Djangonauts 🚀

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants