Added image management command to purge and regenerate renditions

- resolves #8107
pull/8270/head
Hitansh Shah 2022-03-18 18:17:27 +05:30 zatwierdzone przez LB (Ben Johnston)
rodzic 1c7c5cfc0b
commit 0b8a8c2024
9 zmienionych plików z 175 dodań i 0 usunięć

Wyświetl plik

@ -42,6 +42,7 @@ Changelog
* Rename the setting `BASE_URL` (undocumented) to `WAGTAILADMIN_BASE_URL` and add to documentation, `BASE_URL` will be removed in a future release (Sandil Ranasinghe)
* Validate to and from email addresses within form builder pages when using `AbstractEmailForm` (Jake Howard)
* Add `WAGTAILIMAGES_RENDITION_STORAGE` setting to allow an alternative image rendition storage (Heather White)
* Add `wagtail_update_image_renditions` management command to regenerate image renditions or purge all existing renditions (Hitansh Shah, Onno Timmerman, Damian Moore)
* Fix: When using `simple_translations` ensure that the user is redirected to the page edit view when submitting for a single locale (Mitchel Cabuloy)
* Fix: When previewing unsaved changes to `Form` pages, ensure that all added fields are correctly shown in the preview (Joshua Munn)
* Fix: When Documents (e.g. PDFs) have been configured to be served inline via `WAGTAILDOCS_CONTENT_TYPES` & `WAGTAILDOCS_INLINE_CONTENT_TYPES` ensure that the filename is correctly set in the `Content-Disposition` header so that saving the files will use the correct filename (John-Scott Atlakson)

Wyświetl plik

@ -587,6 +587,7 @@ Contributors
* Sandil Ranasinghe
* Caio Jhonny
* Heather White
* Onno Timmerman
Translators
===========

Wyświetl plik

@ -125,3 +125,22 @@ search_garbage_collect
$ ./manage.py search_garbage_collect
Wagtail keeps a log of search queries that are popular on your website. On high traffic websites, this log may get big and you may want to clean out old search queries. This command cleans out all search query logs that are more than one week old (or a number of days configurable through the :ref:`WAGTAILSEARCH_HITS_MAX_AGE <wagtailsearch_hits_max_age>` setting).
.. _wagtail_update_image_renditions:
wagtail_update_image_renditions
-------------------------------
.. code-block:: console
$ ./manage.py wagtail_update_image_renditions
This command provides the ability to regenerate image renditions.
This is useful if you have deployed to a server where the image renditions have not yet been generated or you have changed the underlying image rendition behaviour and need to ensure all renditions are created again.
This does not remove rendition images that are unused, this can be done by clearing the folder using ``rm -rf`` or similar, once this is done you can then use the management command to generate the renditions.
Options:
- **--purge-only** :
This argument will purge all image renditions without regenerating them. They will be regenerated when next requested.

Wyświetl plik

@ -72,6 +72,7 @@ class LandingPage(Page):
* Rename the setting `BASE_URL` (undocumented) to [`WAGTAILADMIN_BASE_URL`](wagtailadmin_base_url) and add to documentation, `BASE_URL` will be removed in a future release (Sandil Ranasinghe)
* Validate to and from email addresses within form builder pages when using `AbstractEmailForm` (Jake Howard)
* Add [`WAGTAILIMAGES_RENDITION_STORAGE`](wagtailimages_rendition_storage) setting to allow an alternative image rendition storage (Heather White)
* Add [`wagtail_update_image_renditions` management command](wagtail_update_image_renditions) to regenerate image renditions or purge all existing renditions (Hitansh Shah, Onno Timmerman, Damian Moore)
### Bug fixes

Wyświetl plik

@ -390,6 +390,14 @@ done from the Django shell:
>>> from wagtail.images.models import Rendition
>>> Rendition.objects.all().delete()
You can also directly use the image management command from the console for regenerating the renditions:
.. code-block:: console
$ ./manage.py wagtail_update_image_renditions --purge
You can read more about this command from :ref:`wagtail_update_image_renditions`
Changing per-tag
^^^^^^^^^^^^^^^^

Wyświetl plik

@ -0,0 +1,56 @@
from django.core.management.base import BaseCommand
from wagtail.images import get_image_model
class Command(BaseCommand):
"""Command to create missing image renditions with the option to remove (purge) any existing ones."""
help = "This command will generate all image renditions, with an option to purge existing renditions first."
def add_arguments(self, parser):
parser.add_argument(
"--purge-only",
action="store_true",
help="Purge all image renditions without regenerating them",
)
def handle(self, *args, **options):
renditions = get_image_model().get_rendition_model().objects.all()
if len(renditions) == 0:
self.stdout.write("No image renditions found.")
return
success_count = 0
if options["purge_only"]:
for rendition in renditions:
try:
rendition_image = rendition.image
rendition.delete()
success_count = success_count + 1
except Exception:
self.stderr.write(
f"Could not purge rendition for {rendition_image.title}"
)
self.stdout.write(
self.style.SUCCESS(
f"Successfully purged {success_count} image rendition(s)"
)
)
else:
for rendition in renditions:
try:
rendition_filter = rendition.filter
rendition_image = rendition.image
rendition.delete()
rendition_image.get_rendition(rendition_filter)
success_count = success_count + 1
except Exception:
self.stderr.write(
f"Could not regenerate rendition for {rendition_image.title}"
)
self.stdout.write(
self.style.SUCCESS(
f"Successfully regenerated {success_count} image rendition(s)"
)
)

Wyświetl plik

@ -0,0 +1,89 @@
import re
from io import StringIO
from django.core import management
from django.test import TestCase
from wagtail.images import get_image_model
from .utils import Image, get_test_image_file
class TestUpdateImageRenditions(TestCase):
def setUp(self):
self.image = Image.objects.create(
title="Test image",
file=get_test_image_file(filename="test_image.png", colour="white"),
)
self.rendition = Image.get_rendition_model().objects.create(
image=self.image,
filter_spec="original",
width=1000,
height=1000,
file=get_test_image_file(
filename="test_rendition.png", colour="white", size=(1000, 1000)
),
)
def delete_renditions(self):
renditions = get_image_model().get_rendition_model().objects.all()
for rendition in renditions:
try:
rendition_image = rendition.image
rendition.delete()
except Exception:
print(f"Could not delete rendition for {rendition_image}")
def run_command(self, **options):
output = StringIO()
management.call_command(
"wagtail_update_image_renditions", stdout=output, **options
)
output.seek(0)
return output
def test_exits_early_for_no_renditions(self):
self.delete_renditions()
# checking when command is called without any arguments
output = self.run_command()
self.assertEqual(output.read(), "No image renditions found.\n")
# checking when command is called with '--purge-only'
output = self.run_command(purge_only=True)
self.assertEqual(output.read(), "No image renditions found.\n")
def test_image_renditions(self):
renditions = get_image_model().get_rendition_model().objects.all()
total_renditions = len(renditions)
output = self.run_command()
reaesc = re.compile(r"\x1b[^m]*m")
output_string = reaesc.sub("", output.read())
# checking if the number of renditions regenerated equal total_renditions
self.assertEqual(
output_string,
f"Successfully regenerated {total_renditions} image rendition(s)\n",
)
# checking if the number of renditions now equal total_renditions
renditions_now = get_image_model().get_rendition_model().objects.all()
total_renditions_now = len(renditions_now)
self.assertEqual(total_renditions_now, total_renditions)
def test_image_renditions_with_purge_only(self):
renditions = get_image_model().get_rendition_model().objects.all()
total_renditions = len(renditions)
output = self.run_command(purge_only=True)
reaesc = re.compile(r"\x1b[^m]*m")
output_string = reaesc.sub("", output.read())
# checking if the number of renditions purged equal total_renditions
self.assertEqual(
output_string,
f"Successfully purged {total_renditions} image rendition(s)\n",
)
# checking if the number of renditions now equal 0
renditions_now = get_image_model().get_rendition_model().objects.all()
total_renditions_now = len(renditions_now)
self.assertEqual(total_renditions_now, 0)