Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove unused snippets delete-multiple view #10099

Closed
wants to merge 4 commits into from

Conversation

laymonage
Copy link
Member

@laymonage laymonage commented Feb 15, 2023

Fixes #10058.

Please check the following:

  • Do the tests still pass?1
  • Does the code comply with the style guide?
    • Run make lint from the Wagtail root.
  • For Python changes: Have you added tests to cover the new/fixed behaviour?
  • For front-end changes: Did you test on all of Wagtail’s supported environments?2
    • Please list the exact browser and operating system versions you tested:
    • Please list which assistive technologies 3 you tested:
  • For new features: Has the documentation been updated accordingly?

Please describe additional details for testing this change.

Footnotes

  1. Development Testing

  2. Browser and device support

  3. Accessibility Target

@squash-labs
Copy link

squash-labs bot commented Feb 15, 2023

Manage this branch in Squash

Test this branch here: https://laymonageremove-unused-b5e1h.squash.io

@@ -1166,7 +1166,7 @@ Called at the beginning of the create snippet view. Works in a similar way to `b

### `after_delete_snippet`

Called when a Snippet is deleted. The callable passed into the hook will receive the model instance(s) as a queryset along with the request object. If the callable returns an `HttpResponse`, that response will be returned immediately to the user, and Wagtail will not proceed to call `redirect()` to the listing view.
Called when a Snippet is deleted. The callable passed into the hook will receive the model instance(s) as a list along with the request object. If the callable returns an `HttpResponse`, that response will be returned immediately to the user, and Wagtail will not proceed to call `redirect()` to the listing view.
Copy link
Member Author

@laymonage laymonage Feb 16, 2023

Choose a reason for hiding this comment

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

This has never been consistent since the hooks were introduced: if there's only one snippet to be deleted, the hook is called with a list, and a queryset otherwise.

The base bulk action view doesn't store a reference to the queryset, it's immediately evaluated and the items are checked for permissions, so we can't call the hook with the queryset:

def get_actionable_objects(self):
objects = []
items_with_no_access = []
object_ids = self.request.GET.getlist("id")
if "all" in object_ids:
object_ids = self.get_all_objects_in_listing_query(
self.request.GET.get("childOf")
)
for obj in self.get_queryset(self.model, object_ids):
if not self.check_perm(obj):
items_with_no_access.append(obj)
else:
objects.append(obj)
return objects, {"items_with_no_access": items_with_no_access}

I was thinking of storing the result in self.queryset somewhere, but weirdly the method is a classmethod:

@classmethod
def get_queryset(cls, model, object_ids):
return get_list_or_404(model, pk__in=object_ids)

And it uses get_list_or_404, which returns a list instead of a QuerySet.

I'm cautious to make any changes to the base bulk action view as it seems out of scope for this PR. Considering that this hook has been partially lost since 4.0, I think having this consistently be a list isn't so bad.

I considered adding a .. versionchanged here but I'm not sure what's the right way to document this, seeing that the hook has never been consistent, and the fact that it hasn't been accessible since 4.0.

@@ -6,5 +6,6 @@
<form action="{{ view.get_delete_url }}" method="POST">
{% csrf_token %}
<input type="submit" value="{% trans 'Yes, delete' %}" class="button serious" />
<a href="{{ view.get_success_url }}" class="button button-secondary">{% trans "No, don't delete" %}</a>
Copy link
Member Author

Choose a reason for hiding this comment

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

I'm surprised this wasn't here before.

I added this so that the snippet delete template can reuse as much of the template as possible, so that I can fully reuse this in #10072

display_name = _("Delete")
action_type = "delete"
aria_label = _("Delete selected snippets")
template_name = "wagtailsnippets/bulk_actions/confirm_bulk_delete.html"
action_priority = 30
classes = {"serious"}

@cached_property
def _actionable_objects(self):
Copy link
Member Author

Choose a reason for hiding this comment

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

This is a bit tangled but I recommend reading the base bulk action view to see why I wrote it this way. In short, the base view's override points aren't as good as they could be.

def run_before_hook(self):
# The method returns a tuple of
# `(objects, {"items_with_no_access": items_with_no_access})`, hence [0]
objects = self.get_actionable_objects()[0]
Copy link
Member Author

Choose a reason for hiding this comment

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

Could also be self._actionable_objects[0], but that would bypass get_actionable_objects() if it has been overridden.

@laymonage
Copy link
Member Author

laymonage commented Mar 1, 2023

FYI @gasman

The hooks situation in summary:

  • Snippets' DeleteView used to handle both single and multiple objects, and the hooks were called for both cases
  • In 4.0, we changed the multiple objects handling to use the bulk action view. The bulk action view has its own before/after hooks (which shares the same {before,after}_bulk_action hook name for all bulk actions, e.g. for pages, and for other non-delete views).
    • The bulk action hooks are triggered just around the deletion code as opposed to the start/end of the view, so the before hook doesn't get called for GET requests.
    • When these changes were made, the snippets' deletion hooks were not called in the bulk action view. This effectively means that the snippet hooks will no longer be triggered when deleting multiple snippets.
  • In this PR, I made it so that the original snippets hooks are called in the snippets' bulk action delete view, at the same points as the original hooks (start/end of the view). This restores the ability to trigger the original snippets hooks when deleting multiple snippets.
    • There's a slight alteration here, as the hooks now receive a list of snippets instead of a queryset, due to how the bulk actions views were implemented. (See review comments above for details to this nuance.)
    • As the code to handle multiple deletion has no longer been used in snippets' DeleteView since 4.0, I removed it.

Copy link
Collaborator

@gasman gasman left a comment

Choose a reason for hiding this comment

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

All looks good to me!

gasman added a commit that referenced this pull request Mar 14, 2023
@gasman
Copy link
Collaborator

gasman commented Mar 14, 2023

Merged in 909e7fe

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
No open projects
Status: Done
Development

Successfully merging this pull request may close these issues.

Remove unused snippets delete-multiple view
2 participants