HackSoftware / Django-Styleguide

Django styleguide used in HackSoft projects

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Where to place the prefetch_related/select_related and similar functions.

EvgenyBorisov opened this issue Β· comments

Hi thank you so much for Styleguide.
You can tell us where to place the working with database optimization logic. For example, prefetch_related/select_related?

An example snippet of the possible scenario can help here.

Thanks!

Best place to put that would be inside selectors.
For every case adapted.
...although you might think on a short term is not DRY to have similar select_related/prefetch.../etc. inside multiple selectors...on a long run the explicit duplication will pay off... the same principle it is applied also for the ad-hoc serialisers from the views.

@EvgenyBorisov πŸ‘‹

I'm glad you find the styteguide useful. That's the main ide πŸ™Œ

As @catalincoroeanu mentioned, you can put those optimizations in the respective selectors.

But I'll go one step further and show you how we do it in more complex scenarios (should be added to the styleguide soon).

Lets imagine you have a selector that returns a list of things:

some_object_list(*args, **kwargs)

And you like to return a list of serialized objects from your API, but the things you want to return combine a bunch of related fields & a bunch of computed fields.

You'll end up doing really strange prefetches & selects in the selector, which are not its main concern.

In that case, we go & create a custom serializer, which is just a function:

def some_objects_serialize(objects: List[SomeObject]):
    pass

and the key thing here is - we usually refetch the entire queryset, with the proper .select_related and .prefetch_related optimizations + any additional inline indexing that needs to be done.

You can also cache here.

It ends up like that:

class SomeObjectSerializer(serializers.Serializer):
    .. fields ..

def some_objects_serialize(objects: List[SomeObject]):
    items = list(SomeObject.select_related(...).prefetch_related(...).filter(id__in=[obj.id for obj in objects]).order_by(...))
   ... inline indexing and other stuff ...

  return [SomeObjectSerializer(item) for obj in items]

This usually lives in serializers.py module & you combine the selector and the serializer in the API:

class SomeApi(SomeApiMixin):
    def get(self, request):
        objects = some_object_list()
        data = some_objects_serialize(objects)
        return Response(data)

Hopefully this gives you a better perspective πŸ™Œ

BTW if you end up needing this selector in other places & you need to have an "optimized" queryset, you can always do an intermediate some_object_prepare / some_object_optimize which does whatever the serializer does, but without the serialization.

Thanks for your answer!!!
πŸ˜ƒ