This task ran me around in circles somewhat, until we found the solution which was literally 1 line of code.
Hopefully if you've stumbled across this blog it may save you losing the time that I did on it.
Let's have a closer look at the problem we're looking to fix here.
So let's say we have a 'ElectricCarPage' and when creating the Page one of our fields is for 'Manufacturer' . Which we are using Snippets for.
On the 'ElectricCarPage' we have this set as 'required' . So one has to be added, however there is a way that the Administration could leave our Page without a Manufacturer ( and possible breaking the site ) . And that would be to do through the Snippets admin and remove the Snippet here
The Solution
___________
So let's cut straight to the chase and give you the solution and then I'll go through the other methods that I'd been through and their gotchas.
+ Add 'on_delete=models.PROTECT' to the Manufacturer field in the 'ElectricCarPage' class. Like
class CarPage(Page):
manufacturer = ForeignKeyField(
"car.Manufacturer",
verbose_name=_("Manufacturer"),
on_delete=models.PROTECT,
related_name="manufacturer",
required=True,
help_text=_(
"The car manufacturer "
),
)
This solution gives you the above screen when you try and delete the Snippet.
THE SOLUTIONS THAT DIDN'T WORK
FIX ATTEMPT 1
on_delete=models.RESTRICT
This is to be added in the GamePage model like so.
class CarPage(Page):
manufacturer = ForeignKeyField(
"car.Manufacturer",
verbose_name=_("Manufacturer"),
on_delete=models.RESTRICT,
related_name="manufacturer",
required=True,
help_text=_(
"The car manufacturer "
),
)
However rather than the screenshot above after you try and delete it throws an error. FIX ATTEMPT 2
The next solution is to use the following code in the Developer Snippet
1def delete(self, *args, **kwargs):
2 CarPage = apps.get_model("cars", "CarPage")
3 carpages = CarPage.objects.filter(manufacturer=self)
4 if carpages:
5 print("@todo Let the user know that they can't delete!")
6 return
7 else:
8 super().delete(*args, **kwargs)
However I met another brick wall when trying to complete this and that’s in that I can output to a message as there is no request to use in this method. I really feel that there should be a way to resolve this method, so if you have one then please let me know, it'd be very useful for future development. FIX ATTEMPT 3
So I then thought I could use this code in a snippet hook .
Hooks — Wagtail Documentation 2.13.4 documentation
1from django.http import HttpResponse
2
3from wagtail.core import hooks
4
5@hooks.register('before_delete_snippet')
6def before_snippet_delete(request, instances):
7 # "instances" is a QuerySet
8 total = len(instances)
9
10 if request.method == 'POST':
11 # Override the deletion behaviour
12 instances.delete()
13
14 return HttpResponse(f"{total} snippets have been deleted", content_type="text/plain")
That hook works if you go through 'Admin>Snippets>Manufacturer' . However I also have a custom Admin menu items for ' Manufacturer' and if you navigate through that way the hook isn't called.
I think this is a real bug for Snippet Hooks. Even if you’re not using separate Admin menu items then whose not to say it may not be requested in the future.
No comments:
Post a Comment