Let's say I want to generate a feed consisting of ItemA
and ItemB
, each item has a "published_date" field and newer items should appear at the top of the feed. However I want to further ensure that the feed always has 2 of ItemA
, followed by 1 of ItemA
, followed by 2 of ItemA
,....regardless of the absolute publish date (i.e. even if the 10 most recent published items were all A, I'd still see B as the third item if there is an instance of it).
How can I achieve something like this in Django?
I was originally doing this something like
# models.pyclass FeedItem(models.Model): id = HashidAutoField(primary_key=True, min_length=8, alphabet="0123456789abcdefghijklmnopqrstuvwxyz") created = models.DateTimeField(auto_now_add=True) updated = models.DateTimeField(auto_now=True, db_index=True) # When is this scheduled to be published publish_date = models.DateTimeField(db_index=True) # Type of Feed Item ITEM_TYPES = Choices((1, 'A', 'ItemA'), (2, 'B', 'ItemB')) # item_type item_type = models.PositiveSmallIntegerField(db_index=True, choices=ITEM_TYPES) def save(self, force_insert=False, force_update=False, using=None, update_fields=None, **kwargs):""" Ensure that when we save each subclass then we get the correct item_type :param force_insert: :param force_update: :param using: :param update_fields: :param kwargs: :return:""" if (self.__class__.__name__ == 'FeedItemA' and self.item_type != self.ITEM_TYPES._identifier_map['A']): self.item_type = self.ITEM_TYPES._identifier_map['A'] elif (self.__class__.__name__ == 'FeedItemB' and self.item_type != self.ITEM_TYPES._identifier_map['B']): self.item_type = self.ITEM_TYPES._identifier_map['B'] class Meta: ordering = ['-publish_date', ]class FeedItemA(FeedItem): ...class FeedItemB(FeedItem): ...
Then a view like
# views.pyclass FeedItemViewSet(viewsets.ReadOnlyModelViewSet): queryset = FeedItem.objects.all() permission_classes = () serialization_info = {'ItemA': FeedSerializationInfo(FeedItemASerializer, 'a', 'artworkfeeditema'),'ItemB': FeedSerializationInfo(FeedItemBSerializer, 'b', 'blogpostfeeditemb'), } filter_backends = (filters.DjangoFilterBackend, rest_filters.OrderingFilter) ordering_fields = ['publish_date', 'updated'] def list(self, request, *args, **kwargs): queryset = self.filter_queryset(self.get_queryset()) page = self.paginate_queryset(queryset) data = [] for item in page: info = self.serialization_info[item.get_item_type_display()] related_item = getattr(item, info.related_name) data.append(info.serializer_class(related_item, context={'request': request}).data) return self.get_paginated_response(data)
This achieves the mixing of the 2 different types, but their order will be solely determined by the publish_date
and we don't get the even mixing of 2 of A, 1 of B etc...
Maybe there is some way to generate a new queryset by popping 2 from the ordered list of A, then 1 from B and so on until both lists are exhausted? then use that queryset for the list viewset?