1

I created the following serializer where I call the function _calculate_image_dimensions twice. I now tried @cachedproperty but doesn't work because I have to pass the values width, height. However, they will not change for get_width and get_height. Is there a way to make sure the calculation only computes once?

class IntegrationImageSerializer(serializers.ModelSerializer):
    width = serializers.SerializerMethodField()
    height = serializers.SerializerMethodField()

    class Meta:
        model = IntegrationImage
        fields = ("image", "width", "height")

    def _calculate_image_dimensions(self, width, height) -> int:
        MAX_IMAGE_SIZE = 350
        aspect_ratio = width / height
        if width > height:
            width = MAX_IMAGE_SIZE
            height = width / aspect_ratio
        else:
            height = MAX_IMAGE_SIZE
            width = height * aspect_ratio
        return round(width), round(height)

    def get_width(self, obj: IntegrationImage) -> int:
        width, height = self._calculate_image_dimensions(
            obj.image.width, obj.image.height
        )
        return width

    def get_height(self, obj: IntegrationImage) -> int:
        width, height = self._calculate_image_dimensions(
            obj.image.width, obj.image.height
        )
        return height

2 Answers 2

1

Make it a free function (outside the class) and decorate it with lru_cache():

from functools import lru_cache

@lru_cache(maxsize=None)
def calculate_image_dimensions(width, height) -> int:
    MAX_IMAGE_SIZE = 350
    aspect_ratio = width / height
    if width > height:
        width = MAX_IMAGE_SIZE
        height = width / aspect_ratio
    else:
        height = MAX_IMAGE_SIZE
        width = height * aspect_ratio
    return round(width), round(height)

This will cache the results for the lifetime of the worker process.

(However, I doubt a function with a couple comparisons and divisions is the performance bottleneck in your app.)

Sign up to request clarification or add additional context in comments.

Comments

0

Initially it seems a bit confusing to me, having a method get_width executing a function to get width and height and then return width only.

Does it make sense to slightly change the API structure by using dimensions field (which might not be actually efficient if width and height are referred to a lot of places)?
If it doesn't require you to change a lot of references to width and height this would call _calculate_image_dimensions only once (which i belive is the problem you are trying to solve?) and you won't need caching.

class IntegrationImageSerializer(serializers.ModelSerializer):
    dimensions = serializers.SerializerMethodField()

    class Meta:
        model = IntegrationImage
        fields = ("image", "dimensions")

    def _calculate_image_dimensions(self, width, height) -> int:
        MAX_IMAGE_SIZE = 350
        aspect_ratio = width / height
        if width > height:
            width = MAX_IMAGE_SIZE
            height = width / aspect_ratio
        else:
            height = MAX_IMAGE_SIZE
            width = height * aspect_ratio
        return round(width), round(height)

    def get_dimensions(self, obj: IntegrationImage) -> int:
        width, height = self._calculate_image_dimensions(
            obj.image.width, obj.image.height
        )
        return {"width": width, "height": height}

1 Comment

Good idea. Looks much better :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.