Django как сортировать по несуществующим полям

В настоящее время у меня есть две модели

posts.models

from django.db import models
from django_prometheus.models import ExportModelOperationsMixin
from users.models import User
from category.models import Category

# Create your models here.

class Post(ExportModelOperationsMixin('post'), models.Model):
  
  header_image = models.ImageField(default='ancean-no-header-image.png')
  title = models.CharField(max_length=100)
  introduce = models.TextField(default='')
  content = models.JSONField('json', null=True, blank=True)
  author = models.ForeignKey(User, on_delete=models.CASCADE, db_column="author", related_name='author')
  category = models.ForeignKey(Category, on_delete=models.SET_NULL, db_column="category", related_name='category', null=True, blank=True)
  wave = models.IntegerField(default=0) # wave field like 'like post' on general SNS
  created_at = models.DateTimeField(null=True)
  updated_at = models.DateTimeField(auto_now=True)
  is_finish = models.BooleanField(default=False)
  is_public = models.BooleanField(default=False)
  
  def __str__(self):
    return f'{self.title} - {self.author}'

category.models

from django.db import models

class Category(models.Model):
  
  name = models.CharField(max_length=30, unique=True)
  
  def __str__(self):
    return f'{self.name}' 

Я хочу создать логику, которая получает количество сообщений в каждой категории и сортирует их в порядке убывания. "http://localhost:8000/api/category?ordering=-post_count" Через url, как указано выше. Я хочу, чтобы это выглядело как результат этого запроса.

        SELECT c.name, count(*) AS post_count FROM category_category c JOIN posts_post p ON (c.id = p.category)
        GROUP BY p.category
        ORDER BY count(*) DESC

Я хочу использовать Django ordering, но post_count является несуществующим полем (модель Category), поэтому я не могу его использовать. Буду благодарен, если вы подскажете мне хороший способ.

category.views

class CategoryView(GenericAPIView, ListModelMixin):

  queryset = Category.objects.all()
  serializer_class = CategorySerializer
  filter_backends = [OrderingFilter]
  ordering_fields = ['post_count']
  authentication_classes = []
  
  def get(self, request, *args, **kwargs):
    return self.list(request)

Я попытался запросить данные, которые хотел получить. Я попытался создать пользовательский запрос.

Параметр related_name=… [Django-doc] - это имя отношения в reverse, то есть от модели Category к модели Post в данном случае. Поэтому (часто) не имеет особого смысла называть его так же, как и прямое отношение. Таким образом, вы можете рассмотреть возможность переименования отношения category в posts:

class Post(ExportModelOperationsMixin('post'), models.Model):
    # …
    category = models.ForeignKey(
        Category,
        on_delete=models.SET_NULL,
        db_column='category',
        related_name='posts',
        null=True,
        blank=True,
    )
    # …

Затем мы можем определить псевдоним count и упорядочить его по количеству сообщений:

from django.db.models import Count


class CategoryView(GenericAPIView, ListModelMixin):
    queryset = Category.objects.annotate(post_count=Count('posts')).order_by(
        '-post_count'
    )
    serializer_class = CategorySerializer
    filter_backends = [OrderingFilter]
    ordering_fields = ['post_count']
    authentication_classes = []
    # …

Note: In order to resolve from what class a method call is routed, the Method Resolution Order [python-doc] is used. Since mixins typically define a function in terms of what is already defined, mixins typically are listed before the base class (there are exceptions), but in this case, you thus reorder the classes to:

class CategoryView(ListModelMixin, GenericAPIView):
    # …
Вернуться на верх