파이썬에서 렌디션 생성하기

Wagtail {% image %} 템플릿 태그로 생성된 원본 이미지의 렌더링된 버전을 “렌디션”이라고 하며, 첫 번째 호출 시 사이트의 [media]/images 디렉토리에 새 이미지 파일로 저장됩니다.

이미지 렌디션은 네이티브 get_rendition() 메서드를 통해 파이썬에서 동적으로 생성할 수도 있습니다. 예를 들면 다음과 같습니다.

newimage = myimage.get_rendition('fill-300x150|jpegquality-60')

myimage 의 파일 이름이 foo.jpg 인 경우, foo.fill-300x150.jpegquality-60.jpg 라는 새 이미지 파일 렌디션이 생성되어 사이트의 [media]/images 디렉토리에 저장됩니다. 인수 옵션은 {% image %} 템플릿 태그의 필터 사양과 동일하며 | 로 구분해야 합니다.

생성된 Rendition 객체에는 url, width, height 와 같이 해당 버전의 이미지에 특정한 속성이 있습니다. 따라서 예를 들어 API 생성기에서 다음과 같이 사용할 수 있습니다.

url = myimage.get_rendition('fill-300x186|jpegquality-60').url

생성된 렌디션이 생성된 원본 이미지에 속하는 title 과 같은 속성은 렌디션의 image 속성을 통해 액세스할 수 있습니다.

    >>> newimage.image.title
    'Blue Sky'
    >>> newimage.image.is_landscape()
    True

참조: 템플릿에서 이미지 사용하는 방법

이미지에 대한 여러 렌디션 생성

네이티브 get_renditions() 메서드를 사용하여 파이썬에서 동일한 이미지의 여러 렌디션을 생성할 수 있습니다. ‘사양’ 문자열 또는 Filter 인스턴스 를 원하는 만큼 수락하고 각 렌디션을 개별적으로 생성하는 것보다 훨씬 효율적으로 일치하는 렌디션 세트를 생성합니다. 예를 들면 다음과 같습니다.

image.get_renditions('width-600', 'height-400', 'fill-300x186|jpegquality-60')

반환 값은 메서드에 제공된 사양을 키로 하는 렌디션 사전입니다. 위 예의 반환 값은 다음과 같습니다.

{
    "width-600": <Rendition: Rendition object (7)>,
    "height-400": <Rendition: Rendition object (8)>,
    "fill-300x186|jpegquality-60": <Rendition: Rendition object (9)>,
}

사양에 preserve-svg 지시문이 포함된 경우 결과 사전 키는 원래 전달된 것이 아니라 렌디션에 사용된 최종 필터 사양(이미지가 SVG인 경우 preserve-svg 지시문 및 SVG에 안전하지 않은 작업 생략)이 됩니다. 이로 인해 목록의 여러 사양이 동일한 최종 값으로 확인될 수 있습니다. 예를 들어 목록에 width-400|format-jpeg|preserve-svgwidth-400|format-webp|preserve-svg 가 포함된 경우 SVG 이미지에 적용하면 둘 다 width-400 으로 줄어듭니다. 이 경우 반환 값은 원래 목록보다 항목 수가 적습니다.

이미지 렌디션 캐싱

Wagtail은 이미지 렌디션 조회를 캐시하여 많은 이미지를 포함하는 페이지의 성능을 향상시킬 수 있습니다.

기본적으로 Wagtail은 “renditions”라는 캐시를 사용하려고 시도합니다. 이러한 캐시가 없으면 기본 캐시를 사용합니다.

이미지 렌디션 미리 가져오기

쿼리셋을 사용하여 이미지 목록 또는 이미지가 있는 객체를 렌더링할 때 단일 추가 쿼리로 필요한 렌디션을 미리 가져올 수 있습니다. 긴 항목 목록이나 각 항목에 여러 렌디션이 사용되는 경우 성능을 크게 향상시킬 수 있습니다.

기존 렌디션 다시 생성

콘솔에서 이미지 관리 명령을 직접 사용하여 렌디션을 다시 생성할 수도 있습니다.

./manage.py wagtail_update_image_renditions --purge

이 명령에 대한 자세한 내용은 wagtail_update_image_renditions에서 읽을 수 있습니다.

이미지 쿼리셋

이미지 쿼리셋으로 작업할 때 Wagtail의 내장 prefetch_renditions 쿼리셋 메서드를 사용하여 필요한 렌디션을 미리 가져올 수 있습니다.

예를 들어 사용자가 업로드한 모든 이미지 목록을 렌더링한다고 가정해 보겠습니다.

def get_images_uploaded_by_user(user):
    return ImageModel.objects.filter(uploaded_by_user=user)

위의 내용은 반환된 이미지의 렌디션을 미리 가져오도록 약간 수정할 수 있습니다.

def get_images_uploaded_by_user(user):
    return ImageModel.objects.filter(uploaded_by_user=user).prefetch_renditions()

위의 내용은 필요하지 않더라도 모든 렌디션을 미리 가져옵니다.

프로젝트의 이미지에 렌디션 수가 매우 많은 경향이 있고 필요한 렌디션을 미리 알고 있는 경우 prefetch_renditions 메서드에 필터 세트를 지정하고 렌더링에 필요한 렌디션만 선택하는 것을 고려할 수 있습니다. 예를 들면 다음과 같습니다.

def get_images_uploaded_by_user(user):
    # 렌더링에 필요한 렌디션만 지정
    return ImageModel.objects.filter(uploaded_by_user=user).prefetch_renditions(
        "fill-700x586", "min-600x400", "max-940x680"
    )

이미지가 아닌 쿼리셋

이미지 모델이 아닌 모델로 작업하는 경우 Django의 내장 prefetch_related() 쿼리셋 메서드를 사용하여 렌디션을 미리 가져올 수 있습니다.

예를 들어 이벤트 목록(각각에 대한 썸네일 이미지 포함)을 렌더링한다고 가정해 보겠습니다. 코드는 다음과 같을 수 있습니다.

def get_events():
    return EventPage.objects.live().select_related("listing_image")

위의 내용은 목록 이미지에 대한 렌디션을 미리 가져오도록 약간 수정할 수 있습니다.

def get_events():
    return EventPage.objects.live().select_related("listing_image").prefetch_related("listing_image__renditions")

필요한 렌디션을 미리 알고 있는 경우 렌디션 쿼리셋을 필터링하여 사용할 수 있습니다.

from django.db.models import Prefetch
from wagtail.images import get_image_model


def get_events():
    Image = get_image_model()
    filters = ["fill-300x186", "fill-600x400", "fill-940x680"]

    # `Prefetch` 는 필요한 렌디션만 가져오는 데 사용됩니다.
    prefetch_images_and_renditions = Prefetch(
        "listing_image",
        queryset=Image.objects.prefetch_renditions(*filters)
    )
    return EventPage.objects.live().prefetch_related(prefetch_images_and_renditions)

렌디션 생성에 관련된 모델 메서드

다음 AbstractImage 모델 메서드는 렌디션을 찾고 생성하는 데 관여합니다. 사용자 정의 이미지 모델을 사용하는 경우 모델에서 이러한 메서드 중 하나의 동작을 재정의하여 사용자 정의할 수 있습니다.

class wagtail.images.models.AbstractImage
get_rendition(filter: Filter | str) AbstractRendition

Returns a Rendition instance with a file field value (an image) reflecting the supplied filter value and focal point values from this object.

Note: If using custom image models, an instance of the custom rendition model will be returned.

find_existing_rendition(filter: Filter) AbstractRendition

Returns an existing Rendition instance with a file field value (an image) reflecting the supplied filter value and focal point values from this object.

If no such rendition exists, a DoesNotExist error is raised for the relevant model.

Note: If using custom image models, an instance of the custom rendition model will be returned.

create_rendition(filter: Filter) AbstractRendition

Creates and returns a Rendition instance with a file field value (an image) reflecting the supplied filter value and focal point values from this object.

This method is usually called by Image.get_rendition(), after first checking that a suitable rendition does not already exist.

Note: If using custom image models, an instance of the custom rendition model will be returned.

get_renditions(*filters: Filter | str) dict[str, AbstractRendition]

Returns a dict of Rendition instances with image files reflecting the supplied filters, keyed by filter spec patterns.

Note: If using custom image models, instances of the custom rendition model will be returned.

find_existing_renditions(*filters: Filter) dict[Filter, AbstractRendition]

Returns a dictionary of existing Rendition instances with file values (images) reflecting the supplied filters and the focal point values from this object.

Filters for which an existing rendition cannot be found are omitted from the return value. If none of the requested renditions have been created before, the return value will be an empty dict.

create_renditions(*filters: Filter) dict[Filter, AbstractRendition]

Creates multiple Rendition instances with image files reflecting the supplied filters, and returns them as a dict keyed by the relevant Filter instance. Where suitable renditions already exist in the database, they will be returned instead, so as not to create duplicates.

This method is usually called by Image.get_renditions(), after first checking that a suitable rendition does not already exist.

Note: If using custom image models, an instance of the custom rendition model will be returned.

generate_rendition_file(filter: Filter, *, source: django.core.files.File = None) django.core.files.File

Generates an in-memory image matching the supplied filter value and focal point value from this object, wraps it in a File object with a suitable filename, and returns it. The return value is used as the file field value for rendition objects saved by AbstractImage.create_rendition().

If the contents of self.file has already been read into memory, the source keyword can be used to provide a reference to the in-memory File, bypassing the need to reload the image contents from storage.

NOTE: The responsibility of generating the new image from the original falls to the supplied filter object. If you want to do anything custom with rendition images (for example, to preserve metadata from the original image), you might want to consider swapping out filter for an instance of a custom Filter subclass of your design.