How to sort objects by function inside model? Django

Question:

In my Product model I have a function total_thumbs_up which count how many users gave thumb up for this product.

class Product(models.Model):
    class ProductObjects(models.Manager):
        def get_queryset(self):
            return super().get_queryset().filter(status='published')
    objects = models.Manager()

    thumbs_up = models.ManyToManyField(
        settings.AUTH_USER_MODEL, related_name="product_thumbs", blank=True)

    def total_thumbs_up(self):
        return self.thumbs_up.count()

In my views file I have two classes like this. I need to GET sorted products by amount of thumbs_up but this line ('-thumbs_up', '0'), doesn’t work fine. I need to have something like ('-total_thumbs_up', '0'),. How can I set it there?

class ProductFilter(django_filters.FilterSet):
    order_by_field = 'ordering'
    ordering = OrderingFilter(
        fields=(
            ('-thumbs_up', '0'),
            ('price', '1'),
            ('-price', '2'),
            ('id', '3'),
            ('-published', '4'),
        )
    )


class ProductViewSet(viewsets.ModelViewSet):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer
    permission_classes = [IsAuthenticatedOrReadOnly]
    pagination_class = Pagination
    filter_backends = [DjangoFilterBackend,
                       filters.OrderingFilter, filters.SearchFilter]
    parser_classes = [MultipartJsonParser, parsers.JSONParser]
    search_fields = ['name']

    filter_class = ProductFilter
    ...

Answers:

Something like:

from django.db.models import Count

ordered_products = Product.objects.all().annotate(
    thumbs_count = Count('thumbs_up')
).order_by('-thumbs_count')

Note, the annotated name thumbs_count must be different than the model the method name total_thumbs_up.

EDIT:

In your ModelViewSet you can do:

class ProductViewSet(...):

    ...
    queryset = Product.objects.all().select_related(
        'thumbs_up'
    ).annotate(
        thumbs_count = Count('thumbs_up')
    ).order_by(
        '-thumbs_count'
    )
    
Answered By: Daniel