From 4424d23fa4478f87df3fc8c56102685214894455 Mon Sep 17 00:00:00 2001 From: Thiago Costa de Souza Date: Thu, 26 May 2022 10:24:47 -0300 Subject: [PATCH] documentation - migrate advanced_topics/images/** to md --- docs/advanced_topics/embeds.md | 4 +- .../{animated_gifs.rst => animated_gifs.md} | 5 +- .../changing_rich_text_representation.md | 56 ++++++ .../changing_rich_text_representation.rst | 57 ------ .../images/custom_image_model.md | 6 +- .../images/feature_detection.md | 125 +++++++++++++ .../images/feature_detection.rst | 151 --------------- docs/advanced_topics/images/focal_points.md | 41 ++++ docs/advanced_topics/images/focal_points.rst | 44 ----- .../images/image_file_formats.md | 44 +++++ .../images/image_file_formats.rst | 47 ----- .../images/image_serve_view.md | 163 ++++++++++++++++ .../images/image_serve_view.rst | 176 ------------------ docs/advanced_topics/images/index.md | 16 ++ docs/advanced_topics/images/index.rst | 16 -- docs/advanced_topics/images/renditions.md | 103 ++++++++++ docs/advanced_topics/images/renditions.rst | 102 ---------- 17 files changed, 554 insertions(+), 602 deletions(-) rename docs/advanced_topics/images/{animated_gifs.rst => animated_gifs.md} (73%) create mode 100644 docs/advanced_topics/images/changing_rich_text_representation.md delete mode 100644 docs/advanced_topics/images/changing_rich_text_representation.rst create mode 100644 docs/advanced_topics/images/feature_detection.md delete mode 100644 docs/advanced_topics/images/feature_detection.rst create mode 100644 docs/advanced_topics/images/focal_points.md delete mode 100644 docs/advanced_topics/images/focal_points.rst create mode 100644 docs/advanced_topics/images/image_file_formats.md delete mode 100644 docs/advanced_topics/images/image_file_formats.rst create mode 100644 docs/advanced_topics/images/image_serve_view.md delete mode 100644 docs/advanced_topics/images/image_serve_view.rst create mode 100644 docs/advanced_topics/images/index.md delete mode 100644 docs/advanced_topics/images/index.rst create mode 100644 docs/advanced_topics/images/renditions.md delete mode 100644 docs/advanced_topics/images/renditions.rst diff --git a/docs/advanced_topics/embeds.md b/docs/advanced_topics/embeds.md index 32edc64dc9..0426f96f5e 100644 --- a/docs/advanced_topics/embeds.md +++ b/docs/advanced_topics/embeds.md @@ -19,7 +19,7 @@ You can use any of the following methods to call the module: Wagtail's default rich text editor has a "media" icon that allows embeds to be placed into rich text. You don't have to do anything to enable this; just make -sure the rich text field's content is being passed through the ``|richtext`` +sure the rich text field's content is being passed through the `|richtext` filter in the template as this is what calls the embeds module to fetch and nest the embed code. @@ -182,7 +182,6 @@ perform the request. Wagtail will not try to run any other finder, even if the chosen one didn't return an embed. - (facebook_and_instagram_embeds)= ### Facebook and Instagram @@ -235,7 +234,6 @@ By passing `'omitscript': True` in the configuration, you can indicate that thes tags should be omitted from the embed HTML. Note that you will then have to take care of loading this script yourself. - (Embedly)= ### Embed.ly diff --git a/docs/advanced_topics/images/animated_gifs.rst b/docs/advanced_topics/images/animated_gifs.md similarity index 73% rename from docs/advanced_topics/images/animated_gifs.rst rename to docs/advanced_topics/images/animated_gifs.md index 51f226c4e2..b8993e09e8 100644 --- a/docs/advanced_topics/images/animated_gifs.rst +++ b/docs/advanced_topics/images/animated_gifs.md @@ -1,11 +1,10 @@ -Animated GIF support -==================== +# Animated GIF support Pillow, Wagtail's default image library, doesn't support animated GIFs. To get animated GIF support, you will have to -`install Wand `_. +[install Wand](https://docs.wand-py.org/en/0.6.7/guide/install.html). Wand is a binding to ImageMagick so make sure that has been installed as well. When installed, Wagtail will automatically use Wand for resizing GIF diff --git a/docs/advanced_topics/images/changing_rich_text_representation.md b/docs/advanced_topics/images/changing_rich_text_representation.md new file mode 100644 index 0000000000..c575730f99 --- /dev/null +++ b/docs/advanced_topics/images/changing_rich_text_representation.md @@ -0,0 +1,56 @@ +(changing_rich_text_representation)= + +# Changing rich text representation + +The HTML representation of an image in rich text can be customised - for example, to display captions or custom fields. + +To do this requires subclassing `Format` (see [](rich_text_image_formats)), and overriding its `image_to_html` method. + +You may then register formats of your subclass using `register_image_format` as usual. + +```python +# image_formats.py +from wagtail.images.formats import Format, register_image_format + + +class SubclassedImageFormat(Format): + + def image_to_html(self, image, alt_text, extra_attributes=None): + + custom_html = # the custom HTML representation of your image here + # in Format, the image's rendition.img_tag(extra_attributes) is used to generate the HTML + # representation + + return custom_html + + +register_image_format( + SubclassedImageFormat('subclassed_format', 'Subclassed Format', classnames, filter_spec) +) +``` + +As an example, let's say you want the alt text to be displayed as a caption for the image as well: + +```python +# image_formats.py +from django.utils.html import format_html +from wagtail.images.formats import Format, register_image_format + + +class CaptionedImageFormat(Format): + + def image_to_html(self, image, alt_text, extra_attributes=None): + + default_html = super().image_to_html(image, alt_text, extra_attributes) + + return format_html("{}
{}
", default_html, alt_text) + + +register_image_format( + CaptionedImageFormat('captioned_fullwidth', 'Full width captioned', 'bodytext-image', 'width-750') +) +``` + +```{note} +Any custom HTML image features will not be displayed in the Draftail editor, only on the published page. +``` diff --git a/docs/advanced_topics/images/changing_rich_text_representation.rst b/docs/advanced_topics/images/changing_rich_text_representation.rst deleted file mode 100644 index 0458006144..0000000000 --- a/docs/advanced_topics/images/changing_rich_text_representation.rst +++ /dev/null @@ -1,57 +0,0 @@ -.. _changing_rich_text_representation: - -================================= -Changing rich text representation -================================= - -The HTML representation of an image in rich text can be customised - for example, to display captions or custom fields. - -To do this requires subclassing ``Format`` (see :ref:`rich_text_image_formats`), and overriding its ``image_to_html`` method. - -You may then register formats of your subclass using ``register_image_format`` as usual. - -.. code-block:: python - - # image_formats.py - from wagtail.images.formats import Format, register_image_format - - - class SubclassedImageFormat(Format): - - def image_to_html(self, image, alt_text, extra_attributes=None): - - custom_html = # the custom HTML representation of your image here - # in Format, the image's rendition.img_tag(extra_attributes) is used to generate the HTML - # representation - - return custom_html - - - register_image_format( - SubclassedImageFormat('subclassed_format', 'Subclassed Format', classnames, filter_spec) - ) - -As an example, let's say you want the alt text to be displayed as a caption for the image as well: - -.. code-block:: python - - # image_formats.py - from django.utils.html import format_html - from wagtail.images.formats import Format, register_image_format - - - class CaptionedImageFormat(Format): - - def image_to_html(self, image, alt_text, extra_attributes=None): - - default_html = super().image_to_html(image, alt_text, extra_attributes) - - return format_html("{}
{}
", default_html, alt_text) - - - register_image_format( - CaptionedImageFormat('captioned_fullwidth', 'Full width captioned', 'bodytext-image', 'width-750') - ) - -.. note:: - Any custom HTML image features will not be displayed in the Draftail editor, only on the published page. diff --git a/docs/advanced_topics/images/custom_image_model.md b/docs/advanced_topics/images/custom_image_model.md index 28f6833e86..6416e249f0 100644 --- a/docs/advanced_topics/images/custom_image_model.md +++ b/docs/advanced_topics/images/custom_image_model.md @@ -7,8 +7,8 @@ to images. To do this, you need to add two models to your project: -- The image model itself that inherits from `wagtail.images.models.AbstractImage`. This is where you would add your additional fields -- The renditions model that inherits from `wagtail.images.models.AbstractRendition`. This is used to store renditions for the new model. +- The image model itself that inherits from `wagtail.images.models.AbstractImage`. This is where you would add your additional fields +- The renditions model that inherits from `wagtail.images.models.AbstractRendition`. This is used to store renditions for the new model. Here's an example: @@ -66,4 +66,4 @@ work as before but would need to be updated in order to see any new images. .. autofunction:: get_image_model .. autofunction:: get_image_model_string -``` \ No newline at end of file +``` diff --git a/docs/advanced_topics/images/feature_detection.md b/docs/advanced_topics/images/feature_detection.md new file mode 100644 index 0000000000..2939b20d69 --- /dev/null +++ b/docs/advanced_topics/images/feature_detection.md @@ -0,0 +1,125 @@ +(image_feature_detection)= + +# Feature Detection + +Wagtail has the ability to automatically detect faces and features inside your images and crop the images to those features. + +Feature detection uses third-party tools to detect faces/features in an image when the image is uploaded. The detected features are stored internally as a focal point in the `focal_point_{x, y, width, height}` fields on the `Image` model. These fields are used by the `fill` image filter when an image is rendered in a template to crop the image. + +## Installation + +Two third-party tools are known to work with Wagtail: One based on [OpenCV](https://opencv.org/) for general feature detection and one based on [Rustface](https://github.com/torchbox/rustface-py/) for face detection. + +### OpenCV on Debian/Ubuntu + +Feature detection requires [OpenCV](https://opencv.org/) which can be a bit tricky to install as it's not currently pip-installable. + +There is more than one way to install these components, but in each case you will need to test that both OpenCV itself _and_ the Python interface have been correctly installed. + +#### Install `opencv-python` + +[opencv-python](https://pypi.org/project/opencv-python/) is available on PyPI. +It includes a Python interface to OpenCV, as well as the statically-built OpenCV binaries themselves. + +To install: + +```console +$ pip install opencv-python +``` + +Depending on what else is installed on your system, this may be all that is required. On lighter-weight Linux systems, you may need to identify and install missing system libraries (for example, a slim version of Debian Stretch requires `libsm6 libxrender1 libxext6` to be installed with `apt`). + +#### Install a system-level package + +A system-level package can take care of all of the required components. Check what is available for your operating system. For example, [python-opencv](https://packages.debian.org/stretch/python-opencv) is available for Debian; it installs OpenCV itself, and sets up Python bindings. + +However, it may make incorrect assumptions about how you're using Python (for example, which version you're using) - test as described below. + +#### Testing the installation + +Test the installation: + +```python +python3 +>>> import cv2 +``` + +An error such as: + +```python +ImportError: libSM.so.6: cannot open shared object file: No such file or directory +``` + +indicates that a required system library (in this case `libsm6`) has not been installed. + +On the other hand, + +```python +ModuleNotFoundError: No module named 'cv2' +``` + +means that the Python components have not been set up correctly in your Python environment. + +If you don't get an import error, installation has probably been successful. + +### Rustface + +[Rustface](https://github.com/torchbox/rustface-py/) is Python library with prebuilt wheel files provided for Linux and macOS. Although implemented in Rust it is pip-installable: + +```console +$ pip install wheel +$ pip install rustface +``` + +#### Registering with Willow + +Rustface provides a plug-in that needs to be registered with [Willow](https://github.com/wagtail/Willow). + +This should be done somewhere that gets run on application startup: + +```python +from willow.registry import registry +import rustface.willow + +registry.register_plugin(rustface.willow) +``` + +For example, in an app's [AppConfig.ready](https://docs.djangoproject.com/en/2.2/ref/applications/#django.apps.AppConfig.ready). + +## Cropping + +The face detection algorithm produces a focal area that is tightly cropped to the face rather than the whole head. + +For images with a single face this can be okay in some cases, e.g. thumbnails, it might be overly tight for "headshots". +Image renditions can encompass more of the head by reducing the crop percentage (`-c`), at the end of the resize-rule, down to as low as 0%: + +```html+django +{% image page.photo fill-200x200-c0 %} +``` + +## Switching on feature detection in Wagtail + +Once installed, you need to set the `WAGTAILIMAGES_FEATURE_DETECTION_ENABLED` setting to `True` to automatically detect faces/features whenever a new image is uploaded in to Wagtail or when an image without a focal point is saved (this is done via a pre-save signal handler): + +```python +# settings.py + +WAGTAILIMAGES_FEATURE_DETECTION_ENABLED = True +``` + +## Manually running feature detection + +If you already have images in your Wagtail site and would like to run feature detection on them, or you want to apply feature detection selectively when the `WAGTAILIMAGES_FEATURE_DETECTION_ENABLED` is set to `False` you can run it manually using the `get_suggested_focal_point()` method on the `Image` model. + +For example, you can manually run feature detection on all images by running the following code in the python shell: + +```python +from wagtail.images import get_image_model + +Image = get_image_model() + +for image in Image.objects.all(): + if not image.has_focal_point(): + image.set_focal_point(image.get_suggested_focal_point()) + image.save() +``` diff --git a/docs/advanced_topics/images/feature_detection.rst b/docs/advanced_topics/images/feature_detection.rst deleted file mode 100644 index 0d77590d34..0000000000 --- a/docs/advanced_topics/images/feature_detection.rst +++ /dev/null @@ -1,151 +0,0 @@ -.. _image_feature_detection: - -Feature Detection -================= - -Wagtail has the ability to automatically detect faces and features inside your images and crop the images to those features. - -Feature detection uses third-party tools to detect faces/features in an image when the image is uploaded. The detected features are stored internally as a focal point in the ``focal_point_{x, y, width, height}`` fields on the ``Image`` model. These fields are used by the ``fill`` image filter when an image is rendered in a template to crop the image. - - -Installation ------------- - -Two third-party tools are known to work with Wagtail: One based on OpenCV_ for general feature detection and one based on Rustface_ for face detection. - -.. _OpenCV: https://opencv.org/ - -.. _Rustface: https://github.com/torchbox/rustface-py/ - -OpenCV on Debian/Ubuntu -^^^^^^^^^^^^^^^^^^^^^^^ - -Feature detection requires OpenCV_ which can be a bit tricky to install as it's not currently pip-installable. - -There is more than one way to install these components, but in each case you will need to test that both OpenCV itself *and* the Python interface have been correctly installed. - - -Install ``opencv-python`` -````````````````````````` - -`opencv-python `_ is available on PyPI. -It includes a Python interface to OpenCV, as well as the statically-built OpenCV binaries themselves. - -To install: - -.. code-block:: console - - $ pip install opencv-python - -Depending on what else is installed on your system, this may be all that is required. On lighter-weight Linux systems, you may need to identify and install missing system libraries (for example, a slim version of Debian Stretch requires ``libsm6 libxrender1 libxext6`` to be installed with ``apt``). - - -Install a system-level package -`````````````````````````````` - -A system-level package can take care of all of the required components. Check what is available for your operating system. For example, `python-opencv `_ is available for Debian; it installs OpenCV itself, and sets up Python bindings. - -However, it may make incorrect assumptions about how you're using Python (for example, which version you're using) - test as described below. - - -Testing the installation -```````````````````````` - -Test the installation:: - - python3 - >>> import cv2 - -An error such as:: - - ImportError: libSM.so.6: cannot open shared object file: No such file or directory - -indicates that a required system library (in this case ``libsm6``) has not been installed. - -On the other hand, - -:: - - ModuleNotFoundError: No module named 'cv2' - -means that the Python components have not been set up correctly in your Python environment. - -If you don't get an import error, installation has probably been successful. - - -Rustface -^^^^^^^^ - -Rustface_ is Python library with prebuilt wheel files provided for Linux and macOS. Although implemented in Rust it is pip-installable: - -.. code-block:: console - - $ pip install wheel - $ pip install rustface - - -Registering with Willow -``````````````````````` - -Rustface provides a plug-in that needs to be registered with Willow_. - -This should be done somewhere that gets run on application startup: - -.. code-block:: python - - from willow.registry import registry - import rustface.willow - - registry.register_plugin(rustface.willow) - -For example, in an app's AppConfig.ready_. - -.. _Willow: https://github.com/wagtail/Willow - -.. _AppConfig.ready: https://docs.djangoproject.com/en/2.2/ref/applications/#django.apps.AppConfig.ready - - -Cropping --------- - -The face detection algorithm produces a focal area that is tightly cropped to the face rather than the whole head. - -For images with a single face this can be okay in some cases, e.g. thumbnails, it might be overly tight for "headshots". -Image renditions can encompass more of the head by reducing the crop percentage (``-c``), at the end of the resize-rule, down to as low as 0%: - -.. code-block:: html+django - - {% image page.photo fill-200x200-c0 %} - - -Switching on feature detection in Wagtail ------------------------------------------ - -Once installed, you need to set the ``WAGTAILIMAGES_FEATURE_DETECTION_ENABLED`` setting to ``True`` to automatically detect faces/features whenever a new image is uploaded in to Wagtail or when an image without a focal point is saved (this is done via a pre-save signal handler): - -.. code-block:: python - - # settings.py - - WAGTAILIMAGES_FEATURE_DETECTION_ENABLED = True - - -Manually running feature detection ----------------------------------- - -If you already have images in your Wagtail site and would like to run feature detection on them, or you want to apply feature detection selectively when the ``WAGTAILIMAGES_FEATURE_DETECTION_ENABLED`` is set to ``False`` you can run it manually using the `get_suggested_focal_point()` method on the ``Image`` model. - -For example, you can manually run feature detection on all images by running the following code in the python shell: - -.. code-block:: python - - from wagtail.images import get_image_model - - Image = get_image_model() - - for image in Image.objects.all(): - if not image.has_focal_point(): - image.set_focal_point(image.get_suggested_focal_point()) - image.save() - - diff --git a/docs/advanced_topics/images/focal_points.md b/docs/advanced_topics/images/focal_points.md new file mode 100644 index 0000000000..5fda356138 --- /dev/null +++ b/docs/advanced_topics/images/focal_points.md @@ -0,0 +1,41 @@ +# Focal points + +Focal points are used to indicate to Wagtail the area of an image that contains the subject. +This is used by the `fill` filter to focus the cropping on the subject, and avoid cropping into it. + +Focal points can be defined manually by a Wagtail user, or automatically by using face or feature detection. + +(rendition_background_position_style)= + +## Setting the `background-position` inline style based on the focal point + +When using a Wagtail image as the background of an element, you can use the `.background_position_style` +attribute on the rendition to position the rendition based on the focal point in the image: + +```html+django +{% image page.image width-1024 as image %} + +
+
+``` + +## Accessing the focal point in templates + +You can access the focal point in the template by accessing the `.focal_point` attribute of a rendition: + +```html+django +{% load wagtailimages %} + +{% image myimage width-800 as myrendition %} + +{{ myimage.title }} +``` diff --git a/docs/advanced_topics/images/focal_points.rst b/docs/advanced_topics/images/focal_points.rst deleted file mode 100644 index 2bb75b7135..0000000000 --- a/docs/advanced_topics/images/focal_points.rst +++ /dev/null @@ -1,44 +0,0 @@ -Focal points -============ - -Focal points are used to indicate to Wagtail the area of an image that contains the subject. -This is used by the ``fill`` filter to focus the cropping on the subject, and avoid cropping into it. - -Focal points can be defined manually by a Wagtail user, or automatically by using face or feature detection. - -.. _rendition_background_position_style: - -Setting the ``background-position`` inline style based on the focal point -------------------------------------------------------------------------- - -When using a Wagtail image as the background of an element, you can use the ``.background_position_style`` -attribute on the rendition to position the rendition based on the focal point in the image: - -.. code-block:: html+Django - - {% image page.image width-1024 as image %} - -
-
- -Accessing the focal point in templates --------------------------------------- - -You can access the focal point in the template by accessing the ``.focal_point`` attribute of a rendition: - -.. code-block:: html+Django - - {% load wagtailimages %} - - {% image myimage width-800 as myrendition %} - - {{ myimage.title }} diff --git a/docs/advanced_topics/images/image_file_formats.md b/docs/advanced_topics/images/image_file_formats.md new file mode 100644 index 0000000000..6baa9983c2 --- /dev/null +++ b/docs/advanced_topics/images/image_file_formats.md @@ -0,0 +1,44 @@ +(image_file_formats)= + +# Image file formats + +## Using the picture element + +The [picture element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/picture) +can be used with the `format-` image operation to specify different +image formats and let the browser choose the one it prefers. For example: + +```python +{% load wagtailimages_tags %} + + + {% image myimage width-1000 format-webp as image_webp %} + + + {% image myimage width-1000 format-png as image_png %} + + + {% image myimage width-1000 format-png %} + +``` + +### Customising output formats + +By default all `bmp` and `webp` images are converted to the `png` format +when no image output format is given. + +The default conversion mapping can be changed by setting the +`WAGTAILIMAGES_FORMAT_CONVERSIONS` to a dictionary which maps the input type +to an output type. + +For example: + +```python + WAGTAILIMAGES_FORMAT_CONVERSIONS = { + 'bmp': 'jpeg', + 'webp': 'webp', + } +``` + +will convert `bmp` images to `jpeg` and disable the default `webp` +to `png` conversion. diff --git a/docs/advanced_topics/images/image_file_formats.rst b/docs/advanced_topics/images/image_file_formats.rst deleted file mode 100644 index 8f20f63ebc..0000000000 --- a/docs/advanced_topics/images/image_file_formats.rst +++ /dev/null @@ -1,47 +0,0 @@ -.. _image_file_formats: - -Image file formats -================== - -Using the picture element -------------------------- - -The `picture element `_ -can be used with the ``format-`` image operation to specify different -image formats and let the browser choose the one it prefers. For example: - -.. code-block:: python - - {% load wagtailimages_tags %} - - - {% image myimage width-1000 format-webp as image_webp %} - - - {% image myimage width-1000 format-png as image_png %} - - - {% image myimage width-1000 format-png %} - - -Customising output formats --------------------------- - -By default all ``bmp`` and ``webp`` images are converted to the ``png`` format -when no image output format is given. - -The default conversion mapping can be changed by setting the -``WAGTAILIMAGES_FORMAT_CONVERSIONS`` to a dictionary which maps the input type -to an output type. - -For example: - -.. code-block:: python - - WAGTAILIMAGES_FORMAT_CONVERSIONS = { - 'bmp': 'jpeg', - 'webp': 'webp', - } - -will convert ``bmp`` images to ``jpeg`` and disable the default ``webp`` -to ``png`` conversion. diff --git a/docs/advanced_topics/images/image_serve_view.md b/docs/advanced_topics/images/image_serve_view.md new file mode 100644 index 0000000000..06821d7508 --- /dev/null +++ b/docs/advanced_topics/images/image_serve_view.md @@ -0,0 +1,163 @@ +(using_images_outside_wagtail)= + +# Dynamic image serve view + +In most cases, developers wanting to generate image renditions in Python should use the `get_rendition()` +method. See [](image_renditions). + +If you need to be able to generate image versions for an _external_ system such as a blog or mobile app, +Wagtail provides a view for dynamically generating renditions of images by calling a unique URL. + +The view takes an image id, filter spec and security signature in the URL. If +these parameters are valid, it serves an image file matching that criteria. + +Like the `{% image %}` tag, the rendition is generated on the first call and +subsequent calls are served from a cache. + +## Setup + +Add an entry for the view into your URLs configuration: + +```python +from wagtail.images.views.serve import ServeView + +urlpatterns = [ + ... + + re_path(r'^images/([^/]*)/(\d*)/([^/]*)/[^/]*$', ServeView.as_view(), name='wagtailimages_serve'), + + ... + + # Ensure that the wagtailimages_serve line appears above the default Wagtail page serving route + re_path(r'', include(wagtail_urls)), +] +``` + +## Usage + +### Image URL generator UI + +When the dynamic serve view is enabled, an image URL generator in the admin +interface becomes available automatically. This can be accessed through the edit +page of any image by clicking the "URL generator" button on the right hand side. + +This interface allows editors to generate URLs to cropped versions of the image. + +### Generating dynamic image URLs in Python + +Dynamic image URLs can also be generated using Python code and served to a +client over an API or used directly in the template. + +One advantage of using dynamic image URLs in the template is that they do not +block the initial response while rendering like the `{% image %}` tag does. + +The `generate_image_url` function in `wagtail.images.views.serve` is a convenience +method to generate a dynamic image URL. + +Here's an example of this being used in a view: + +```python +def display_image(request, image_id): + image = get_object_or_404(Image, id=image_id) + + return render(request, 'display_image.html', { + 'image_url': generate_image_url(image, 'fill-100x100') + }) +``` + +Image operations can be chained by joining them with a `|` character: + +```python +return render(request, 'display_image.html', { + 'image_url': generate_image_url(image, 'fill-100x100|jpegquality-40') +}) +``` + +In your templates: + +```html+django +{% load wagtailimages_tags %} +... + + +{% image_url page.photo "width-400" %} + + +{% image_url page.photo "fill-100x100|jpegquality-40" %} + + +{% image_url page.photo "width-400" "mycustomview_serve" %} +``` + +You can pass an optional view name that will be used to serve the image through. The default is `wagtailimages_serve` + +## Advanced configuration + +(image_serve_view_redirect_action)= + +### Making the view redirect instead of serve + +By default, the view will serve the image file directly. This behaviour can be +changed to a 301 redirect instead which may be useful if you host your images +externally. + +To enable this, pass `action='redirect'` into the `ServeView.as_view()` +method in your urls configuration: + +```python +from wagtail.images.views.serve import ServeView + +urlpatterns = [ + ... + + re_path(r'^images/([^/]*)/(\d*)/([^/]*)/[^/]*$', ServeView.as_view(action='redirect'), name='wagtailimages_serve'), +] +``` + +(image_serve_view_sendfile)= + +## Integration with django-sendfile + +[django-sendfile](https://github.com/johnsensible/django-sendfile) offloads the job of transferring the image data to the web +server instead of serving it directly from the Django application. This could +greatly reduce server load in situations where your site has many images being +downloaded but you're unable to use a [](caching_proxy) or a CDN. + +You firstly need to install and configure django-sendfile and configure your +web server to use it. If you haven't done this already, please refer to the +[installation docs](https://github.com/johnsensible/django-sendfile#django-sendfile). + +To serve images with django-sendfile, you can use the `SendFileView` class. +This view can be used out of the box: + +```python +from wagtail.images.views.serve import SendFileView + +urlpatterns = [ + ... + + re_path(r'^images/([^/]*)/(\d*)/([^/]*)/[^/]*$', SendFileView.as_view(), name='wagtailimages_serve'), +] +``` + +You can customise it to override the backend defined in the `SENDFILE_BACKEND` +setting: + +```python +from wagtail.images.views.serve import SendFileView +from project.sendfile_backends import MyCustomBackend + +class MySendFileView(SendFileView): + backend = MyCustomBackend +``` + +You can also customise it to serve private files. For example, if the only need +is to be authenticated (e.g. for Django >= 1.9): + +```python +from django.contrib.auth.mixins import LoginRequiredMixin +from wagtail.images.views.serve import SendFileView + +class PrivateSendFileView(LoginRequiredMixin, SendFileView): + raise_exception = True +``` diff --git a/docs/advanced_topics/images/image_serve_view.rst b/docs/advanced_topics/images/image_serve_view.rst deleted file mode 100644 index bd83bdf39a..0000000000 --- a/docs/advanced_topics/images/image_serve_view.rst +++ /dev/null @@ -1,176 +0,0 @@ -.. _using_images_outside_wagtail: - -======================== -Dynamic image serve view -======================== - -In most cases, developers wanting to generate image renditions in Python should use the ``get_rendition()`` -method. See :ref:`image_renditions`. - -If you need to be able to generate image versions for an *external* system such as a blog or mobile app, -Wagtail provides a view for dynamically generating renditions of images by calling a unique URL. - -The view takes an image id, filter spec and security signature in the URL. If -these parameters are valid, it serves an image file matching that criteria. - -Like the ``{% image %}`` tag, the rendition is generated on the first call and -subsequent calls are served from a cache. - -Setup -===== - -Add an entry for the view into your URLs configuration: - -.. code-block:: python - - from wagtail.images.views.serve import ServeView - - urlpatterns = [ - ... - - re_path(r'^images/([^/]*)/(\d*)/([^/]*)/[^/]*$', ServeView.as_view(), name='wagtailimages_serve'), - - ... - - # Ensure that the wagtailimages_serve line appears above the default Wagtail page serving route - re_path(r'', include(wagtail_urls)), - ] - -Usage -===== - -Image URL generator UI ----------------------- - -When the dynamic serve view is enabled, an image URL generator in the admin -interface becomes available automatically. This can be accessed through the edit -page of any image by clicking the "URL generator" button on the right hand side. - -This interface allows editors to generate URLs to cropped versions of the image. - -Generating dynamic image URLs in Python ---------------------------------------- - -Dynamic image URLs can also be generated using Python code and served to a -client over an API or used directly in the template. - -One advantage of using dynamic image URLs in the template is that they do not -block the initial response while rendering like the ``{% image %}`` tag does. - -The ``generate_image_url`` function in ``wagtail.images.views.serve`` is a convenience -method to generate a dynamic image URL. - -Here's an example of this being used in a view: - -.. code-block:: python - - def display_image(request, image_id): - image = get_object_or_404(Image, id=image_id) - - return render(request, 'display_image.html', { - 'image_url': generate_image_url(image, 'fill-100x100') - }) - - -Image operations can be chained by joining them with a ``|`` character: - -.. code-block:: python - - return render(request, 'display_image.html', { - 'image_url': generate_image_url(image, 'fill-100x100|jpegquality-40') - }) - - -In your templates: - -.. code-block:: html+django - - {% load wagtailimages_tags %} - ... - - - {% image_url page.photo "width-400" %} - - - {% image_url page.photo "fill-100x100|jpegquality-40" %} - - - {% image_url page.photo "width-400" "mycustomview_serve" %} - -You can pass an optional view name that will be used to serve the image through. The default is ``wagtailimages_serve`` - -Advanced configuration -====================== - -.. _image_serve_view_redirect_action: - -Making the view redirect instead of serve ------------------------------------------ - -By default, the view will serve the image file directly. This behaviour can be -changed to a 301 redirect instead which may be useful if you host your images -externally. - -To enable this, pass ``action='redirect'`` into the ``ServeView.as_view()`` -method in your urls configuration: - -.. code-block:: python - - from wagtail.images.views.serve import ServeView - - urlpatterns = [ - ... - - re_path(r'^images/([^/]*)/(\d*)/([^/]*)/[^/]*$', ServeView.as_view(action='redirect'), name='wagtailimages_serve'), - ] - -.. _image_serve_view_sendfile: - -Integration with django-sendfile --------------------------------- - -`django-sendfile`_ offloads the job of transferring the image data to the web -server instead of serving it directly from the Django application. This could -greatly reduce server load in situations where your site has many images being -downloaded but you're unable to use a :ref:`caching_proxy` or a CDN. - -.. _django-sendfile: https://github.com/johnsensible/django-sendfile - -You firstly need to install and configure django-sendfile and configure your -web server to use it. If you haven't done this already, please refer to the -`installation docs `_. - -To serve images with django-sendfile, you can use the ``SendFileView`` class. -This view can be used out of the box: - -.. code-block:: python - - from wagtail.images.views.serve import SendFileView - - urlpatterns = [ - ... - - re_path(r'^images/([^/]*)/(\d*)/([^/]*)/[^/]*$', SendFileView.as_view(), name='wagtailimages_serve'), - ] - -You can customise it to override the backend defined in the ``SENDFILE_BACKEND`` -setting: - -.. code-block:: python - - from wagtail.images.views.serve import SendFileView - from project.sendfile_backends import MyCustomBackend - - class MySendFileView(SendFileView): - backend = MyCustomBackend - -You can also customise it to serve private files. For example, if the only need -is to be authenticated (e.g. for Django >= 1.9): - -.. code-block:: python - - from django.contrib.auth.mixins import LoginRequiredMixin - from wagtail.images.views.serve import SendFileView - - class PrivateSendFileView(LoginRequiredMixin, SendFileView): - raise_exception = True diff --git a/docs/advanced_topics/images/index.md b/docs/advanced_topics/images/index.md new file mode 100644 index 0000000000..5ecbdadf93 --- /dev/null +++ b/docs/advanced_topics/images/index.md @@ -0,0 +1,16 @@ +# Images + +```{toctree} +--- +maxdepth: 2 +--- +renditions +animated_gifs +image_file_formats +custom_image_model +changing_rich_text_representation +feature_detection +image_serve_view +focal_points +title_generation_on_upload +``` diff --git a/docs/advanced_topics/images/index.rst b/docs/advanced_topics/images/index.rst deleted file mode 100644 index 5633d2ae0c..0000000000 --- a/docs/advanced_topics/images/index.rst +++ /dev/null @@ -1,16 +0,0 @@ -Images -====== - - -.. toctree:: - :maxdepth: 2 - - renditions - animated_gifs - image_file_formats - custom_image_model - changing_rich_text_representation - feature_detection - image_serve_view - focal_points - title_generation_on_upload diff --git a/docs/advanced_topics/images/renditions.md b/docs/advanced_topics/images/renditions.md new file mode 100644 index 0000000000..9e0e7be33f --- /dev/null +++ b/docs/advanced_topics/images/renditions.md @@ -0,0 +1,103 @@ +(image_renditions)= + +# Generating renditions in Python + +Rendered versions of original images generated by the Wagtail `{% image %}` template tag are called "renditions", +and are stored as new image files in the site's `[media]/images` directory on the first invocation. + +Image renditions can also be generated dynamically from Python via the native `get_rendition()` method, for example: + +```python +newimage = myimage.get_rendition('fill-300x150|jpegquality-60') +``` + +If `myimage` had a filename of `foo.jpg`, a new rendition of the image file called +`foo.fill-300x150.jpegquality-60.jpg` would be generated and saved into the site's `[media]/images` directory. +Argument options are identical to the `{% image %}` template tag's filter spec, and should be separated with `|`. + +The generated `Rendition` object will have properties specific to that version of the image, such as +`url`, `width` and `height`, so something like this could be used in an API generator, for example: + +```python +url = myimage.get_rendition('fill-300x186|jpegquality-60').url +``` + +Properties belonging to the original image from which the generated Rendition was created, such as `title`, can +be accessed through the Rendition's `image` property: + +```python + >>> newimage.image.title + 'Blue Sky' + >>> newimage.image.is_landscape() + True +``` + +See also: [](image_tag) + +(prefetching_image_renditions)= + +## Prefetching image renditions + +```{versionadded} 3.0 +This following guidance is only applicable in Wagtail versions 3.0 and above. +``` + +When using a queryset to render a list of objects with images, you can make use of Django's built-in `prefetch_related()` queryset method to prefetch the renditions needed for rendering with a single additional query. For long lists of items, or where multiple renditions are used for each item, this can provide a significant boost to performance. + +For example, say you were rendering a list of events (with thumbnail images for each). Your code might look something like this: + +```python +def get_events(): + return EventPage.objects.live().select_related("listing_image") +``` + +The above can be modified slightly to prefetch the renditions for listing images: + +```python +def get_events(): + return EventPage.objects.live().select_related("listing_image").prefetch_related("listing_image__renditions") +``` + +If images in your project tend to have very large numbers of renditions, and you know in advance the ones you need, you might want to consider using a `Prefetch` object to select only the renditions you need for rendering. For example: + +```python +from django.db.models import Prefetch +from wagtail.images import get_image_model + + +def get_events(): + # These are the renditions required for rendering + renditions_queryset = get_image_model().get_rendition_model().objects.filter( + filter_spec__in=["fill-300x186", "fill-600x400", "fill-940x680"] + ) + + # `Prefetch` is used to fetch only the required renditions + return EventPage.objects.live().select_related("listing_image").prefetch_related( + Prefetch("listing_image__renditions", queryset=renditions_queryset) + ) +``` + +(image_rendition_methods)= + +## Model methods involved in rendition generation + +```{versionadded} 3.0 +The following method references are only applicable to Wagtail versions 3.0 and above. +``` + +The following `AbstractImage` model methods are involved in finding and generating a renditions. If using a custom image model, you can customise the behaviour of either of these methods by overriding them on your model: + +```{eval-rst} +.. automodule:: wagtail.images.models + +.. class:: AbstractImage + :noindex: + + .. automethod:: get_rendition + + .. automethod:: find_existing_rendition + + .. automethod:: create_rendition + + .. automethod:: generate_rendition_file +``` diff --git a/docs/advanced_topics/images/renditions.rst b/docs/advanced_topics/images/renditions.rst deleted file mode 100644 index ad9b42c56e..0000000000 --- a/docs/advanced_topics/images/renditions.rst +++ /dev/null @@ -1,102 +0,0 @@ -.. _image_renditions: - -Generating renditions in Python -===================================== - -Rendered versions of original images generated by the Wagtail ``{% image %}`` template tag are called "renditions", -and are stored as new image files in the site's ``[media]/images`` directory on the first invocation. - -Image renditions can also be generated dynamically from Python via the native ``get_rendition()`` method, for example: - -.. code-block:: python - - newimage = myimage.get_rendition('fill-300x150|jpegquality-60') - -If ``myimage`` had a filename of ``foo.jpg``, a new rendition of the image file called -``foo.fill-300x150.jpegquality-60.jpg`` would be generated and saved into the site's ``[media]/images`` directory. -Argument options are identical to the ``{% image %}`` template tag's filter spec, and should be separated with ``|``. - -The generated ``Rendition`` object will have properties specific to that version of the image, such as -``url``, ``width`` and ``height``, so something like this could be used in an API generator, for example: - -.. code-block:: python - - url = myimage.get_rendition('fill-300x186|jpegquality-60').url - -Properties belonging to the original image from which the generated Rendition was created, such as ``title``, can -be accessed through the Rendition's ``image`` property: - -.. code-block:: python - - >>> newimage.image.title - 'Blue Sky' - >>> newimage.image.is_landscape() - True - -See also: :ref:`image_tag` - -.. _prefetching_image_renditions: - -Prefetching image renditions ----------------------------- - -.. versionadded:: 3.0 - This following guidance is only applicable in Wagtail versions 3.0 and above. - -When using a queryset to render a list of objects with images, you can make use of Django's built-in ``prefetch_related()`` queryset method to prefetch the renditions needed for rendering with a single additional query. For long lists of items, or where multiple renditions are used for each item, this can provide a significant boost to performance. - -For example, say you were rendering a list of events (with thumbnail images for each). Your code might look something like this: - -.. code-block:: python - - def get_events(): - return EventPage.objects.live().select_related("listing_image") - -The above can be modified slightly to prefetch the renditions for listing images: - -.. code-block:: python - - def get_events(): - return EventPage.objects.live().select_related("listing_image").prefetch_related("listing_image__renditions") - -If images in your project tend to have very large numbers of renditions, and you know in advance the ones you need, you might want to consider using a ``Prefetch`` object to select only the renditions you need for rendering. For example: - -.. code-block:: python - - from django.db.models import Prefetch - from wagtail.images import get_image_model - - - def get_events(): - # These are the renditions required for rendering - renditions_queryset = get_image_model().get_rendition_model().objects.filter( - filter_spec__in=["fill-300x186", "fill-600x400", "fill-940x680"] - ) - - # `Prefetch` is used to fetch only the required renditions - return EventPage.objects.live().select_related("listing_image").prefetch_related( - Prefetch("listing_image__renditions", queryset=renditions_queryset) - ) - -.. _image_rendition_methods: - -Model methods involved in rendition generation ----------------------------------------------- - -.. versionadded:: 3.0 - The following method references are only applicable to Wagtail versions 3.0 and above. - -The following ``AbstractImage`` model methods are involved in finding and generating a renditions. If using a custom image model, you can customise the behaviour of either of these methods by overriding them on your model: - -.. automodule:: wagtail.images.models - -.. class:: AbstractImage - :noindex: - - .. automethod:: get_rendition - - .. automethod:: find_existing_rendition - - .. automethod:: create_rendition - - .. automethod:: generate_rendition_file