Skip to content
Advertisement

About converting custom functions in models into annotations in custom managers/querysets

Being new to Django, I’m starting to care a bit about performance of my web application.

I’m trying to transform many of my custom functions / properties which were originally in my models to querysets within custom managers.

in my model I have:

class Shape(models.Model): 
    @property
    def nb_color(self):
        return 1 if self.colors=='' else int(1+sum(self.colors.upper().count(x) for x in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'))

    def __str__(self):
        return  self.name + "-" + self.constraints

    @property
    def image_url(self):
        return format_html(f'{settings.SVG_DIR}/{self}.svg')

    @property
    def image_src(self):
        return format_html('<img src="{url}"|urlencode />'.format(url = self.image_url))

    def image_display(self):
        return format_html(f'<a href="/static/SVG_shapes/{self}">{self.image_src}"</a>')

But I’m not clear on a few points:

1/ is there any pros or cons declaring with the propriety decorator in a django model?

2/ what is the cost of calling a function/property in term of database calls

and therefore, is there an added value to use custom managers / querysets and define annotations to simulate my functions at that level?

3/ how would you suggest me to transform my image & nb_color functions into annotations

Thanks in advance

PS: For the image related functions, I mostly figured it out:

self.annotate(image_url = Concat(Value(join(settings.SVG_DIR,'')), F('fullname'), Value('.svg'), output_field=CharField()),
                        image_src = Concat(Value('<img src="'), F('image_url'), Value('"|urlencode />'), output_field=CharField()),
                        image_display = Concat(Value('<a href="'+ settings.SVG_DIR+'/'), F('fullname'), Value('.svg">'),F('image_src'), Value('</a>'), output_field=CharField()),
                        )

I am however having an issue for the display of image_src through:

readonly_fields=['image']
def image(self, obj):
    return format_html(obj.image_src)

it doesn’t seem to find the image while the adress is ok.

If anybody has an idea…

Advertisement

Answer

With now 1.5 years more experience, I’ll try to answer my newbie questions for the next ones who may have the same questions poping into their minds.

1/ is there any pros or cons declaring with the propriety decorator in a django model?

No cons that I could see so far.

It allows the data to be retrieved as a property of the model (my_shape.image_url), instead of having to call the corresponding method (my_shape.image_url())

However, for different purposes, one my prefer to have a callable (the method) instead of a property

2/ what is the cost of calling a function/property in term of database calls

No extra calling to the database if the data it needs as input are already available, or are themselves attributes of the instance object (fields / properties / methods that don’t require input from outside the instance object)

However, if external data are needed, a database call will be generated for each of them.

For this reason, it can be valuable to cache the result of such a property by using the @cached_property decorator instead of the @property decorator

The only thing needed to use cached properties is the following import:

from django.utils.functional import cached_property 

After being called for the first time, the cached property will remain available at no extra cost during all the lifetime of the object instance, and its content can be manipulated like any other property / variable:

and therefore, is there an added value to use custom managers / querysets and define annotations to simulate my functions at that level?

In my understanding and practice so far, it is not uncommon to replicate the same functionality in both property & managers

The reason is that properties are easily available when we are interested only in one specific object instance, while when you are interested into comparing / retrieving a given property for a range of objects, it is much more efficient to calculate & annotate this property for the whole queryset, for instance through using model managers

My give-away would be: For a given model, (1) try to put all the business logic concerning a single object instance into model methods / properties (2) and all the business logic concerning a range of objects into model managers

3/ how would you suggest me to transform my image & nb_color functions into annotations

Already answered in previous answer

1 People found this is helpful
Advertisement