From 4a5a6e36043f9f36336931b544293d1824ee9049 Mon Sep 17 00:00:00 2001 From: Radoslav Georgiev Date: Tue, 16 Nov 2021 17:26:51 +0200 Subject: [PATCH] Expand section for advanced serialization --- README.md | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 74128bc..a63a0f2 100644 --- a/README.md +++ b/README.md @@ -864,7 +864,75 @@ The implementation of `inline_serializer` can be found [here](https://github.com ### Advanced serialization -*Coming soon* +Sometimes, the end result of an API can be quite complex. Sometimes, we want to optimize the queries that we do and the optimization itself is quite complex. + +Trying to stick with just an `OutputSerializer` in that case might limit our options. + +In those cases, we can implement our output serialization as a function, and have the optimizations we need there. + +Lets take this API as an example: + +```python +class SomeGenericFeedApi(BaseApi): + def get(self, request): + feed = some_feed_get( + user=request.user, + ) + + data = some_feed_serialize(feed) + + return Response(data) +``` + +In this scenario, `some_feed_get` has the responsibility of returning a list of feed items (can be ORM objects, can be just IDs). + +And we want to push the complexity of serializing this feed, in an optimal manner, to the serializer function - `some_feed_serialize`. + +This means we don't have to do any unnecessary prefetches & optimizations in `some_feed_get`. + +Here's an example of `some_feed_serialize`: + +```python +class FeedItemSerializer(serializers.Serializer): + ... some fields here ... + calculated_field = serializers.IntegerField(source="_calculated_field") + + +def some_feed_serialize(feed: List[FeedItem]): + feed_ids = [feed_item.id for feed_item in feed] + + # Refetch items with more optimizations + # Based on the relations that are going in + objects = FeedItem.objects.select_related( + ... as complex as you want ... + ).prefetch_related( + ... as complex as you want ... + ).filter( + id__in=feed_ids + ).order_by( + "-some_timestamp" + ) + + some_cache = get_some_cache(feed_ids) + + result = [] + + for feed_item in objects: + # An example, adding additional fields for the serializer + # That are based on values outside of our current object + # This may be some optimization to save queries + feed_item._calculated_field = some_cache.get(feed_item.id) + + result.append(FeedItemSerializer(feed_item).data) + + return result +``` + +As you can see, this is a pretty generic example, but the idea is simple: + +1. Refetch your data, with the needed joins & prefetches. +1. Fetch or build in-memory caches, that will save you queries for specific computed values. +1. Return a result, that's ready to be an API response. ## Urls